diff --git a/_vc2017.bat b/_vc2017.bat index a634bfe1..2caabd5d 100644 --- a/_vc2017.bat +++ b/_vc2017.bat @@ -1,10 +1,14 @@ @echo off +set PROJDIR=%~dp0 + for /f "usebackq tokens=*" %%i in (`"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere" -latest -products * -requires Microsoft.Component.MSBuild -property installationPath`) do ( set InstallDir=%%i ) if exist "%InstallDir%\MSBuild\15.0\Bin\MSBuild.exe" ( call "%InstallDir%\Common7\Tools\VsDevCmd.bat" + pushd %PROJDIR% "%InstallDir%\MSBuild\15.0\Bin\MSBuild.exe" /m:%NUMBER_OF_PROCESSORS% /nr:false /nologo /verbosity:minimal %* -) \ No newline at end of file + popd +) diff --git a/include/caryll/element.h b/include/caryll/element.h index fdad04e9..82bfc73c 100644 --- a/include/caryll/element.h +++ b/include/caryll/element.h @@ -6,6 +6,8 @@ #include #include +#include "ownership.h" + // We assume all T have trivial move constructors. #define caryll_T(T) \ void (*init)(MODIFY T *); \ diff --git a/include/caryll/vector.h b/include/caryll/vector.h index 9fa66130..4f1aece9 100644 --- a/include/caryll/vector.h +++ b/include/caryll/vector.h @@ -21,10 +21,11 @@ void (*initN)(MODIFY __TV * arr, size_t n); \ void (*initCapN)(MODIFY __TV * arr, size_t n); \ __TV *(*createN)(size_t n); \ + void (*fill)(MODIFY __TV * arr, size_t n); \ void (*clear)(MODIFY __TV * arr); \ void (*push)(MODIFY __TV * arr, MOVE __T obj); \ + void (*shrinkToFit)(MODIFY __TV * arr); \ __T (*pop)(MODIFY __TV * arr); \ - void (*fill)(MODIFY __TV * arr, size_t n); \ void (*disposeItem)(MODIFY __TV * arr, size_t n); \ void (*filterEnv)(MODIFY __TV * arr, bool (*fn)(const __T *x, void *env), void *env); \ void (*sort)(MODIFY __TV * arr, int (*fn)(const __T *a, const __T *b)); diff --git a/include/dep/uthash.h b/include/dep/uthash.h index ed69c0c5..6bf5688c 100644 --- a/include/dep/uthash.h +++ b/include/dep/uthash.h @@ -1,5 +1,5 @@ /* -Copyright (c) 2003-2014, Troy D. Hanson http://troydhanson.github.com/uthash/ +Copyright (c) 2003-2017, Troy D. Hanson http://troydhanson.github.com/uthash/ All rights reserved. Redistribution and use in source and binary forms, with or without @@ -24,39 +24,42 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef UTHASH_H #define UTHASH_H -#include /* memcmp,strlen */ +#define UTHASH_VERSION 2.0.2 + +#include /* memcmp, memset, strlen */ #include /* ptrdiff_t */ -#include /* exit() */ +#include /* exit */ /* These macros use decltype or the earlier __typeof GNU extension. As decltype is only available in newer compilers (VS2010 or gcc 4.3+ when compiling c++ source) this code uses whatever method is needed or, for VS2008 where neither is available, uses casting workarounds. */ +#if !defined(DECLTYPE) && !defined(NO_DECLTYPE) #if defined(_MSC_VER) /* MS compiler */ #if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ #define DECLTYPE(x) (decltype(x)) #else /* VS2008 or older (or VS2010 in C mode) */ #define NO_DECLTYPE -#define DECLTYPE(x) #endif -#elif defined(__BORLANDC__) || defined(__LCC__) || defined(__WATCOMC__) +#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__) #define NO_DECLTYPE -#define DECLTYPE(x) #else /* GNU, Sun and other compilers */ #define DECLTYPE(x) (__typeof(x)) #endif +#endif #ifdef NO_DECLTYPE +#define DECLTYPE(x) #define DECLTYPE_ASSIGN(dst,src) \ do { \ char **_da_dst = (char**)(&(dst)); \ *_da_dst = (char*)(src); \ -} while(0) +} while (0) #else #define DECLTYPE_ASSIGN(dst,src) \ do { \ (dst) = DECLTYPE(dst)(src); \ -} while(0) +} while (0) #endif /* a number of the hash function use uint32_t which isn't defined on Pre VS2010 */ @@ -76,17 +79,21 @@ typedef unsigned int uint32_t; typedef unsigned char uint8_t; #endif -#define UTHASH_VERSION 1.9.9 - -#ifndef uthash_fatal -#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */ -#endif #ifndef uthash_malloc #define uthash_malloc(sz) malloc(sz) /* malloc fcn */ #endif #ifndef uthash_free #define uthash_free(ptr,sz) free(ptr) /* free fcn */ #endif +#ifndef uthash_bzero +#define uthash_bzero(a,n) memset(a,'\0',n) +#endif +#ifndef uthash_memcmp +#define uthash_memcmp(a,b,n) memcmp(a,b,n) +#endif +#ifndef uthash_strlen +#define uthash_strlen(s) strlen(s) +#endif #ifndef uthash_noexpand_fyi #define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ @@ -95,37 +102,89 @@ typedef unsigned char uint8_t; #define uthash_expand_fyi(tbl) /* can be defined to log expands */ #endif +#ifndef HASH_NONFATAL_OOM +#define HASH_NONFATAL_OOM 0 +#endif + +#if HASH_NONFATAL_OOM +/* malloc failures can be recovered from */ + +#ifndef uthash_nonfatal_oom +#define uthash_nonfatal_oom(obj) do {} while (0) /* non-fatal OOM error */ +#endif + +#define HASH_RECORD_OOM(oomed) do { (oomed) = 1; } while (0) +#define IF_HASH_NONFATAL_OOM(x) x + +#else +/* malloc failures result in lost memory, hash tables are unusable */ + +#ifndef uthash_fatal +#define uthash_fatal(msg) exit(-1) /* fatal OOM error */ +#endif + +#define HASH_RECORD_OOM(oomed) uthash_fatal("out of memory") +#define IF_HASH_NONFATAL_OOM(x) + +#endif + /* initial number of buckets */ #define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */ #define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */ #define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */ -/* calculate the element whose hash handle address is hhe */ +/* calculate the element whose hash handle address is hhp */ #define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) +/* calculate the hash handle from element address elp */ +#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle *)(((char*)(elp)) + ((tbl)->hho))) -#define HASH_FIND(hh,head,keyptr,keylen,out) \ +#define HASH_ROLLBACK_BKT(hh, head, itemptrhh) \ do { \ - out=NULL; \ - if (head != NULL) { \ - unsigned _hf_bkt,_hf_hashv; \ - HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \ - if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv) != 0) { \ - HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \ - keyptr,keylen,out); \ - } \ + struct UT_hash_handle *_hd_hh_item = (itemptrhh); \ + unsigned _hd_bkt; \ + HASH_TO_BKT(_hd_hh_item->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + (head)->hh.tbl->buckets[_hd_bkt].count++; \ + _hd_hh_item->hh_next = NULL; \ + _hd_hh_item->hh_prev = NULL; \ +} while (0) + +#define HASH_VALUE(keyptr,keylen,hashv) \ +do { \ + HASH_FCN(keyptr, keylen, hashv); \ +} while (0) + +#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out) \ +do { \ + (out) = NULL; \ + if (head) { \ + unsigned _hf_bkt; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \ + if (HASH_BLOOM_TEST((head)->hh.tbl, hashval) != 0) { \ + HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \ + } \ } \ } while (0) +#define HASH_FIND(hh,head,keyptr,keylen,out) \ +do { \ + unsigned _hf_hashv; \ + HASH_VALUE(keyptr, keylen, _hf_hashv); \ + HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \ +} while (0) + #ifdef HASH_BLOOM #define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM) #define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL) -#define HASH_BLOOM_MAKE(tbl) \ +#define HASH_BLOOM_MAKE(tbl,oomed) \ do { \ (tbl)->bloom_nbits = HASH_BLOOM; \ (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ - if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \ - memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \ - (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ + if (!(tbl)->bloom_bv) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ + (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ + } \ } while (0) #define HASH_BLOOM_FREE(tbl) \ @@ -137,80 +196,241 @@ do { #define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U))) #define HASH_BLOOM_ADD(tbl,hashv) \ - HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1U))) + HASH_BLOOM_BITSET((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) #define HASH_BLOOM_TEST(tbl,hashv) \ - HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1U))) + HASH_BLOOM_BITTEST((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) #else -#define HASH_BLOOM_MAKE(tbl) +#define HASH_BLOOM_MAKE(tbl,oomed) #define HASH_BLOOM_FREE(tbl) #define HASH_BLOOM_ADD(tbl,hashv) #define HASH_BLOOM_TEST(tbl,hashv) (1) #define HASH_BLOOM_BYTELEN 0U #endif -#define HASH_MAKE_TABLE(hh,head) \ -do { \ - (head)->hh.tbl = (UT_hash_table*)uthash_malloc( \ - sizeof(UT_hash_table)); \ - if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \ - memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \ - (head)->hh.tbl->tail = &((head)->hh); \ - (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ - (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ - (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ - (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ - HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ - if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \ - memset((head)->hh.tbl->buckets, 0, \ - HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ - HASH_BLOOM_MAKE((head)->hh.tbl); \ - (head)->hh.tbl->signature = HASH_SIGNATURE; \ -} while(0) +#define HASH_MAKE_TABLE(hh,head,oomed) \ +do { \ + (head)->hh.tbl = (UT_hash_table*)uthash_malloc(sizeof(UT_hash_table)); \ + if (!(head)->hh.tbl) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head)->hh.tbl->tail = &((head)->hh); \ + (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ + (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ + (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ + (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ + HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ + (head)->hh.tbl->signature = HASH_SIGNATURE; \ + if (!(head)->hh.tbl->buckets) { \ + HASH_RECORD_OOM(oomed); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + } else { \ + uthash_bzero((head)->hh.tbl->buckets, \ + HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_MAKE((head)->hh.tbl, oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (oomed) { \ + uthash_free((head)->hh.tbl->buckets, \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + } \ + ) \ + } \ + } \ +} while (0) + +#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \ +do { \ + (replaced) = NULL; \ + HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ + if (replaced) { \ + HASH_DELETE(hh, head, replaced); \ + } \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \ +} while (0) -#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ - HASH_ADD_KEYPTR(hh,head,&((add)->fieldname),keylen_in,add) +#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \ +do { \ + (replaced) = NULL; \ + HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ + if (replaced) { \ + HASH_DELETE(hh, head, replaced); \ + } \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \ +} while (0) #define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ do { \ - replaced=NULL; \ - HASH_FIND(hh,head,&((add)->fieldname),keylen_in,replaced); \ - if (replaced!=NULL) { \ - HASH_DELETE(hh,head,replaced); \ + unsigned _hr_hashv; \ + HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ + HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \ +} while (0) + +#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn) \ +do { \ + unsigned _hr_hashv; \ + HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ + HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \ +} while (0) + +#define HASH_APPEND_LIST(hh, head, add) \ +do { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ + (head)->hh.tbl->tail->next = (add); \ + (head)->hh.tbl->tail = &((add)->hh); \ +} while (0) + +#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ +do { \ + do { \ + if (cmpfcn(DECLTYPE(head)(_hs_iter), add) > 0) { \ + break; \ + } \ + } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ +} while (0) + +#ifdef NO_DECLTYPE +#undef HASH_AKBI_INNER_LOOP +#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ +do { \ + char *_hs_saved_head = (char*)(head); \ + do { \ + DECLTYPE_ASSIGN(head, _hs_iter); \ + if (cmpfcn(head, add) > 0) { \ + DECLTYPE_ASSIGN(head, _hs_saved_head); \ + break; \ + } \ + DECLTYPE_ASSIGN(head, _hs_saved_head); \ + } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ +} while (0) +#endif + +#if HASH_NONFATAL_OOM + +#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ +do { \ + if (!(oomed)) { \ + unsigned _ha_bkt; \ + (head)->hh.tbl->num_items++; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ + if (oomed) { \ + HASH_ROLLBACK_BKT(hh, head, &(add)->hh); \ + HASH_DELETE_HH(hh, head, &(add)->hh); \ + (add)->hh.tbl = NULL; \ + uthash_nonfatal_oom(add); \ + } else { \ + HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ + HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ + } \ + } else { \ + (add)->hh.tbl = NULL; \ + uthash_nonfatal_oom(add); \ } \ - HASH_ADD(hh,head,fieldname,keylen_in,add); \ -} while(0) +} while (0) + +#else + +#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ +do { \ + unsigned _ha_bkt; \ + (head)->hh.tbl->num_items++; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ + HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ + HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ +} while (0) + +#endif + + +#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \ +do { \ + IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ + (add)->hh.hashv = (hashval); \ + (add)->hh.key = (char*) (keyptr); \ + (add)->hh.keylen = (unsigned) (keylen_in); \ + if (!(head)) { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh, add, _ha_oomed); \ + IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ + (head) = (add); \ + IF_HASH_NONFATAL_OOM( } ) \ + } else { \ + void *_hs_iter = (head); \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_AKBI_INNER_LOOP(hh, head, add, cmpfcn); \ + if (_hs_iter) { \ + (add)->hh.next = _hs_iter; \ + if (((add)->hh.prev = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev)) { \ + HH_FROM_ELMT((head)->hh.tbl, (add)->hh.prev)->next = (add); \ + } else { \ + (head) = (add); \ + } \ + HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev = (add); \ + } else { \ + HASH_APPEND_LIST(hh, head, add); \ + } \ + } \ + HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ + HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE_INORDER"); \ +} while (0) + +#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn) \ +do { \ + unsigned _hs_hashv; \ + HASH_VALUE(keyptr, keylen_in, _hs_hashv); \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \ +} while (0) + +#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn) + +#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn) \ + HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn) + +#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add) \ +do { \ + IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ + (add)->hh.hashv = (hashval); \ + (add)->hh.key = (char*) (keyptr); \ + (add)->hh.keylen = (unsigned) (keylen_in); \ + if (!(head)) { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh, add, _ha_oomed); \ + IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ + (head) = (add); \ + IF_HASH_NONFATAL_OOM( } ) \ + } else { \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_APPEND_LIST(hh, head, add); \ + } \ + HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ + HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE"); \ +} while (0) #define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ do { \ - unsigned _ha_bkt; \ - (add)->hh.next = NULL; \ - (add)->hh.key = (char*)(keyptr); \ - (add)->hh.keylen = (unsigned)(keylen_in); \ - if (!(head)) { \ - head = (add); \ - (head)->hh.prev = NULL; \ - HASH_MAKE_TABLE(hh,head); \ - } else { \ - (head)->hh.tbl->tail->next = (add); \ - (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ - (head)->hh.tbl->tail = &((add)->hh); \ - } \ - (head)->hh.tbl->num_items++; \ - (add)->hh.tbl = (head)->hh.tbl; \ - HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \ - (add)->hh.hashv, _ha_bkt); \ - HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \ - HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \ - HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \ - HASH_FSCK(hh,head); \ -} while(0) - -#define HASH_TO_BKT( hashv, num_bkts, bkt ) \ + unsigned _ha_hashv; \ + HASH_VALUE(keyptr, keylen_in, _ha_hashv); \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add); \ +} while (0) + +#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add) \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add) + +#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ + HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add) + +#define HASH_TO_BKT(hashv,num_bkts,bkt) \ do { \ bkt = ((hashv) & ((num_bkts) - 1U)); \ -} while(0) +} while (0) /* delete "delptr" from the hash table. * "the usual" patch-up process for the app-order doubly-linked-list. @@ -225,48 +445,44 @@ do { * scratch pointer rather than through the repointed (users) symbol. */ #define HASH_DELETE(hh,head,delptr) \ + HASH_DELETE_HH(hh, head, &(delptr)->hh) + +#define HASH_DELETE_HH(hh,head,delptrhh) \ do { \ - struct UT_hash_handle *_hd_hh_del; \ - if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \ - uthash_free((head)->hh.tbl->buckets, \ - (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ - HASH_BLOOM_FREE((head)->hh.tbl); \ - uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ - head = NULL; \ + struct UT_hash_handle *_hd_hh_del = (delptrhh); \ + if ((_hd_hh_del->prev == NULL) && (_hd_hh_del->next == NULL)) { \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head) = NULL; \ + } else { \ + unsigned _hd_bkt; \ + if (_hd_hh_del == (head)->hh.tbl->tail) { \ + (head)->hh.tbl->tail = HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev); \ + } \ + if (_hd_hh_del->prev != NULL) { \ + HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev)->next = _hd_hh_del->next; \ } else { \ - unsigned _hd_bkt; \ - _hd_hh_del = &((delptr)->hh); \ - if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \ - (head)->hh.tbl->tail = \ - (UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ - (head)->hh.tbl->hho); \ - } \ - if ((delptr)->hh.prev != NULL) { \ - ((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ - (head)->hh.tbl->hho))->next = (delptr)->hh.next; \ - } else { \ - DECLTYPE_ASSIGN(head,(delptr)->hh.next); \ - } \ - if (_hd_hh_del->next != NULL) { \ - ((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next + \ - (head)->hh.tbl->hho))->prev = \ - _hd_hh_del->prev; \ - } \ - HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ - HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ - (head)->hh.tbl->num_items--; \ + DECLTYPE_ASSIGN(head, _hd_hh_del->next); \ + } \ + if (_hd_hh_del->next != NULL) { \ + HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->next)->prev = _hd_hh_del->prev; \ } \ - HASH_FSCK(hh,head); \ + HASH_TO_BKT(_hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + HASH_DEL_IN_BKT((head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ + (head)->hh.tbl->num_items--; \ + } \ + HASH_FSCK(hh, head, "HASH_DELETE_HH"); \ } while (0) - /* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ #define HASH_FIND_STR(head,findstr,out) \ - HASH_FIND(hh,head,findstr,(unsigned)strlen(findstr),out) + HASH_FIND(hh,head,findstr,(unsigned)uthash_strlen(findstr),out) #define HASH_ADD_STR(head,strfield,add) \ - HASH_ADD(hh,head,strfield[0],(unsigned int)strlen(add->strfield),add) + HASH_ADD(hh,head,strfield[0],(unsigned)uthash_strlen(add->strfield),add) #define HASH_REPLACE_STR(head,strfield,add,replaced) \ - HASH_REPLACE(hh,head,strfield[0],(unsigned)strlen(add->strfield),add,replaced) + HASH_REPLACE(hh,head,strfield[0],(unsigned)uthash_strlen(add->strfield),add,replaced) #define HASH_FIND_INT(head,findint,out) \ HASH_FIND(hh,head,findint,sizeof(int),out) #define HASH_ADD_INT(head,intfield,add) \ @@ -287,59 +503,56 @@ do { */ #ifdef HASH_DEBUG #define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) -#define HASH_FSCK(hh,head) \ -do { \ - struct UT_hash_handle *_thh; \ - if (head) { \ - unsigned _bkt_i; \ - unsigned _count; \ - char *_prev; \ - _count = 0; \ - for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \ - unsigned _bkt_count = 0; \ - _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ - _prev = NULL; \ - while (_thh) { \ - if (_prev != (char*)(_thh->hh_prev)) { \ - HASH_OOPS("invalid hh_prev %p, actual %p\n", \ - _thh->hh_prev, _prev ); \ - } \ - _bkt_count++; \ - _prev = (char*)(_thh); \ - _thh = _thh->hh_next; \ - } \ - _count += _bkt_count; \ - if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ - HASH_OOPS("invalid bucket count %u, actual %u\n", \ - (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ - } \ - } \ - if (_count != (head)->hh.tbl->num_items) { \ - HASH_OOPS("invalid hh item count %u, actual %u\n", \ - (head)->hh.tbl->num_items, _count ); \ - } \ - /* traverse hh in app order; check next/prev integrity, count */ \ - _count = 0; \ - _prev = NULL; \ - _thh = &(head)->hh; \ - while (_thh) { \ - _count++; \ - if (_prev !=(char*)(_thh->prev)) { \ - HASH_OOPS("invalid prev %p, actual %p\n", \ - _thh->prev, _prev ); \ - } \ - _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ - _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \ - (head)->hh.tbl->hho) : NULL ); \ - } \ - if (_count != (head)->hh.tbl->num_items) { \ - HASH_OOPS("invalid app item count %u, actual %u\n", \ - (head)->hh.tbl->num_items, _count ); \ +#define HASH_FSCK(hh,head,where) \ +do { \ + struct UT_hash_handle *_thh; \ + if (head) { \ + unsigned _bkt_i; \ + unsigned _count = 0; \ + char *_prev; \ + for (_bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; ++_bkt_i) { \ + unsigned _bkt_count = 0; \ + _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ + _prev = NULL; \ + while (_thh) { \ + if (_prev != (char*)(_thh->hh_prev)) { \ + HASH_OOPS("%s: invalid hh_prev %p, actual %p\n", \ + (where), (void*)_thh->hh_prev, (void*)_prev); \ } \ + _bkt_count++; \ + _prev = (char*)(_thh); \ + _thh = _thh->hh_next; \ + } \ + _count += _bkt_count; \ + if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ + HASH_OOPS("%s: invalid bucket count %u, actual %u\n", \ + (where), (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ + } \ } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("%s: invalid hh item count %u, actual %u\n", \ + (where), (head)->hh.tbl->num_items, _count); \ + } \ + _count = 0; \ + _prev = NULL; \ + _thh = &(head)->hh; \ + while (_thh) { \ + _count++; \ + if (_prev != (char*)_thh->prev) { \ + HASH_OOPS("%s: invalid prev %p, actual %p\n", \ + (where), (void*)_thh->prev, (void*)_prev); \ + } \ + _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ + _thh = (_thh->next ? HH_FROM_ELMT((head)->hh.tbl, _thh->next) : NULL); \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("%s: invalid app item count %u, actual %u\n", \ + (where), (head)->hh.tbl->num_items, _count); \ + } \ + } \ } while (0) #else -#define HASH_FSCK(hh,head) +#define HASH_FSCK(hh,head,where) #endif /* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to @@ -348,9 +561,9 @@ do { #ifdef HASH_EMIT_KEYS #define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ do { \ - unsigned _klen = fieldlen; \ - write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ - write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \ + unsigned _klen = fieldlen; \ + write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ + write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \ } while (0) #else #define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) @@ -364,44 +577,41 @@ do { #endif /* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */ -#define HASH_BER(key,keylen,num_bkts,hashv,bkt) \ +#define HASH_BER(key,keylen,hashv) \ do { \ - unsigned _hb_keylen=(unsigned)keylen; \ - const unsigned char *_hb_key=(const unsigned char*)(key); \ + unsigned _hb_keylen = (unsigned)keylen; \ + const unsigned char *_hb_key = (const unsigned char*)(key); \ (hashv) = 0; \ while (_hb_keylen-- != 0U) { \ - (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \ + (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \ } \ - bkt = (hashv) & (num_bkts-1U); \ } while (0) /* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ -#define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \ +#define HASH_SAX(key,keylen,hashv) \ do { \ unsigned _sx_i; \ - const unsigned char *_hs_key=(const unsigned char*)(key); \ + const unsigned char *_hs_key = (const unsigned char*)(key); \ hashv = 0; \ - for(_sx_i=0; _sx_i < keylen; _sx_i++) { \ - hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ + for (_sx_i=0; _sx_i < keylen; _sx_i++) { \ + hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ } \ - bkt = hashv & (num_bkts-1U); \ } while (0) /* FNV-1a variation */ -#define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \ +#define HASH_FNV(key,keylen,hashv) \ do { \ unsigned _fn_i; \ - const unsigned char *_hf_key=(const unsigned char*)(key); \ - hashv = 2166136261U; \ - for(_fn_i=0; _fn_i < keylen; _fn_i++) { \ - hashv = hashv ^ _hf_key[_fn_i]; \ - hashv = hashv * 16777619U; \ + const unsigned char *_hf_key = (const unsigned char*)(key); \ + (hashv) = 2166136261U; \ + for (_fn_i=0; _fn_i < keylen; _fn_i++) { \ + hashv = hashv ^ _hf_key[_fn_i]; \ + hashv = hashv * 16777619U; \ } \ - bkt = hashv & (num_bkts-1U); \ -} while(0) +} while (0) -#define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \ +#define HASH_OAT(key,keylen,hashv) \ do { \ unsigned _ho_i; \ const unsigned char *_ho_key=(const unsigned char*)(key); \ @@ -414,8 +624,7 @@ do { hashv += (hashv << 3); \ hashv ^= (hashv >> 11); \ hashv += (hashv << 15); \ - bkt = hashv & (num_bkts-1U); \ -} while(0) +} while (0) #define HASH_JEN_MIX(a,b,c) \ do { \ @@ -430,7 +639,7 @@ do { c -= a; c -= b; c ^= ( b >> 15 ); \ } while (0) -#define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \ +#define HASH_JEN(key,keylen,hashv) \ do { \ unsigned _hj_i,_hj_j,_hj_k; \ unsigned const char *_hj_key=(unsigned const char*)(key); \ @@ -455,21 +664,20 @@ do { } \ hashv += (unsigned)(keylen); \ switch ( _hj_k ) { \ - case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \ - case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \ - case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \ - case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \ - case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \ - case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \ - case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \ - case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \ - case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \ - case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \ - case 1: _hj_i += _hj_key[0]; \ + case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \ + case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \ + case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \ + case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \ + case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \ + case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \ + case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \ + case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \ + case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \ + case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \ + case 1: _hj_i += _hj_key[0]; \ } \ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ - bkt = hashv & (num_bkts-1U); \ -} while(0) +} while (0) /* The Paul Hsieh hash function */ #undef get16bits @@ -482,7 +690,7 @@ do { #define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ +(uint32_t)(((const uint8_t *)(d))[0]) ) #endif -#define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \ +#define HASH_SFH(key,keylen,hashv) \ do { \ unsigned const char *_sfh_key=(unsigned const char*)(key); \ uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \ @@ -516,15 +724,14 @@ do { hashv += hashv >> 1; \ } \ \ - /* Force "avalanching" of final 127 bits */ \ - hashv ^= hashv << 3; \ - hashv += hashv >> 5; \ - hashv ^= hashv << 4; \ - hashv += hashv >> 17; \ - hashv ^= hashv << 25; \ - hashv += hashv >> 6; \ - bkt = hashv & (num_bkts-1U); \ -} while(0) + /* Force "avalanching" of final 127 bits */ \ + hashv ^= hashv << 3; \ + hashv += hashv >> 5; \ + hashv ^= hashv << 4; \ + hashv += hashv >> 17; \ + hashv ^= hashv << 25; \ + hashv += hashv >> 6; \ +} while (0) #ifdef HASH_USING_NO_STRICT_ALIASING /* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads. @@ -566,9 +773,9 @@ do { \ _h ^= _h >> 13; \ _h *= 0xc2b2ae35u; \ _h ^= _h >> 16; \ -} while(0) +} while (0) -#define HASH_MUR(key,keylen,num_bkts,hashv,bkt) \ +#define HASH_MUR(key,keylen,hashv) \ do { \ const uint8_t *_mur_data = (const uint8_t*)(key); \ const int _mur_nblocks = (int)(keylen) / 4; \ @@ -579,7 +786,7 @@ do { \ const uint8_t *_mur_tail; \ const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+(_mur_nblocks*4)); \ int _mur_i; \ - for(_mur_i = -_mur_nblocks; _mur_i!=0; _mur_i++) { \ + for (_mur_i = -_mur_nblocks; _mur_i != 0; _mur_i++) { \ _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \ _mur_k1 *= _mur_c1; \ _mur_k1 = MUR_ROTL32(_mur_k1,15); \ @@ -591,7 +798,8 @@ do { \ } \ _mur_tail = (const uint8_t*)(_mur_data + (_mur_nblocks*4)); \ _mur_k1=0; \ - switch((keylen) & 3U) { \ + switch ((keylen) & 3U) { \ + case 0: break; \ case 3: _mur_k1 ^= (uint32_t)_mur_tail[2] << 16; /* FALLTHROUGH */ \ case 2: _mur_k1 ^= (uint32_t)_mur_tail[1] << 8; /* FALLTHROUGH */ \ case 1: _mur_k1 ^= (uint32_t)_mur_tail[0]; \ @@ -603,53 +811,68 @@ do { \ _mur_h1 ^= (uint32_t)(keylen); \ MUR_FMIX(_mur_h1); \ hashv = _mur_h1; \ - bkt = hashv & (num_bkts-1U); \ -} while(0) +} while (0) #endif /* HASH_USING_NO_STRICT_ALIASING */ -/* key comparison function; return 0 if keys equal */ -#define HASH_KEYCMP(a,b,len) memcmp(a,b,(unsigned long)(len)) - /* iterate over items in a known bucket to find desired item */ -#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \ +#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out) \ do { \ - if (head.hh_head != NULL) { DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head)); } \ - else { out=NULL; } \ - while (out != NULL) { \ - if ((out)->hh.keylen == (keylen_in)) { \ - if ((HASH_KEYCMP((out)->hh.key,keyptr,keylen_in)) == 0) { break; } \ + if ((head).hh_head != NULL) { \ + DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head)); \ + } else { \ + (out) = NULL; \ + } \ + while ((out) != NULL) { \ + if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) { \ + if (uthash_memcmp((out)->hh.key, keyptr, keylen_in) == 0) { \ + break; \ + } \ } \ - if ((out)->hh.hh_next != NULL) { DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,(out)->hh.hh_next)); } \ - else { out = NULL; } \ - } \ -} while(0) + if ((out)->hh.hh_next != NULL) { \ + DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next)); \ + } else { \ + (out) = NULL; \ + } \ + } \ +} while (0) /* add an item to a bucket */ -#define HASH_ADD_TO_BKT(head,addhh) \ -do { \ - head.count++; \ - (addhh)->hh_next = head.hh_head; \ - (addhh)->hh_prev = NULL; \ - if (head.hh_head != NULL) { (head).hh_head->hh_prev = (addhh); } \ - (head).hh_head=addhh; \ - if ((head.count >= ((head.expand_mult+1U) * HASH_BKT_CAPACITY_THRESH)) \ - && ((addhh)->tbl->noexpand != 1U)) { \ - HASH_EXPAND_BUCKETS((addhh)->tbl); \ - } \ -} while(0) +#define HASH_ADD_TO_BKT(head,hh,addhh,oomed) \ +do { \ + UT_hash_bucket *_ha_head = &(head); \ + _ha_head->count++; \ + (addhh)->hh_next = _ha_head->hh_head; \ + (addhh)->hh_prev = NULL; \ + if (_ha_head->hh_head != NULL) { \ + _ha_head->hh_head->hh_prev = (addhh); \ + } \ + _ha_head->hh_head = (addhh); \ + if ((_ha_head->count >= ((_ha_head->expand_mult + 1U) * HASH_BKT_CAPACITY_THRESH)) \ + && !(addhh)->tbl->noexpand) { \ + HASH_EXPAND_BUCKETS(addhh,(addhh)->tbl, oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (oomed) { \ + HASH_DEL_IN_BKT(head,addhh); \ + } \ + ) \ + } \ +} while (0) /* remove an item from a given bucket */ -#define HASH_DEL_IN_BKT(hh,head,hh_del) \ - (head).count--; \ - if ((head).hh_head == hh_del) { \ - (head).hh_head = hh_del->hh_next; \ - } \ - if (hh_del->hh_prev) { \ - hh_del->hh_prev->hh_next = hh_del->hh_next; \ - } \ - if (hh_del->hh_next) { \ - hh_del->hh_next->hh_prev = hh_del->hh_prev; \ - } +#define HASH_DEL_IN_BKT(head,delhh) \ +do { \ + UT_hash_bucket *_hd_head = &(head); \ + _hd_head->count--; \ + if (_hd_head->hh_head == (delhh)) { \ + _hd_head->hh_head = (delhh)->hh_next; \ + } \ + if ((delhh)->hh_prev) { \ + (delhh)->hh_prev->hh_next = (delhh)->hh_next; \ + } \ + if ((delhh)->hh_next) { \ + (delhh)->hh_next->hh_prev = (delhh)->hh_prev; \ + } \ +} while (0) /* Bucket expansion has the effect of doubling the number of buckets * and redistributing the items into the new buckets. Ideally the @@ -680,53 +903,55 @@ do { * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) * */ -#define HASH_EXPAND_BUCKETS(tbl) \ -do { \ - unsigned _he_bkt; \ - unsigned _he_bkt_i; \ - struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ - UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ - _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ - 2UL * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ - if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \ - memset(_he_new_buckets, 0, \ - 2UL * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ - tbl->ideal_chain_maxlen = \ - (tbl->num_items >> (tbl->log2_num_buckets+1U)) + \ - (((tbl->num_items & ((tbl->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \ - tbl->nonideal_items = 0; \ - for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \ - { \ - _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \ - while (_he_thh != NULL) { \ - _he_hh_nxt = _he_thh->hh_next; \ - HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2U, _he_bkt); \ - _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \ - if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \ - tbl->nonideal_items++; \ - _he_newbkt->expand_mult = _he_newbkt->count / \ - tbl->ideal_chain_maxlen; \ - } \ - _he_thh->hh_prev = NULL; \ - _he_thh->hh_next = _he_newbkt->hh_head; \ - if (_he_newbkt->hh_head != NULL) { _he_newbkt->hh_head->hh_prev = \ - _he_thh; } \ - _he_newbkt->hh_head = _he_thh; \ - _he_thh = _he_hh_nxt; \ +#define HASH_EXPAND_BUCKETS(hh,tbl,oomed) \ +do { \ + unsigned _he_bkt; \ + unsigned _he_bkt_i; \ + struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ + UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ + _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ + 2UL * (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ + if (!_he_new_buckets) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero(_he_new_buckets, \ + 2UL * (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ + (tbl)->ideal_chain_maxlen = \ + ((tbl)->num_items >> ((tbl)->log2_num_buckets+1U)) + \ + ((((tbl)->num_items & (((tbl)->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \ + (tbl)->nonideal_items = 0; \ + for (_he_bkt_i = 0; _he_bkt_i < (tbl)->num_buckets; _he_bkt_i++) { \ + _he_thh = (tbl)->buckets[ _he_bkt_i ].hh_head; \ + while (_he_thh != NULL) { \ + _he_hh_nxt = _he_thh->hh_next; \ + HASH_TO_BKT(_he_thh->hashv, (tbl)->num_buckets * 2U, _he_bkt); \ + _he_newbkt = &(_he_new_buckets[_he_bkt]); \ + if (++(_he_newbkt->count) > (tbl)->ideal_chain_maxlen) { \ + (tbl)->nonideal_items++; \ + _he_newbkt->expand_mult = _he_newbkt->count / (tbl)->ideal_chain_maxlen; \ + } \ + _he_thh->hh_prev = NULL; \ + _he_thh->hh_next = _he_newbkt->hh_head; \ + if (_he_newbkt->hh_head != NULL) { \ + _he_newbkt->hh_head->hh_prev = _he_thh; \ } \ + _he_newbkt->hh_head = _he_thh; \ + _he_thh = _he_hh_nxt; \ + } \ } \ - uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ - tbl->num_buckets *= 2U; \ - tbl->log2_num_buckets++; \ - tbl->buckets = _he_new_buckets; \ - tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \ - (tbl->ineff_expands+1U) : 0U; \ - if (tbl->ineff_expands > 1U) { \ - tbl->noexpand=1; \ - uthash_noexpand_fyi(tbl); \ + uthash_free((tbl)->buckets, (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ + (tbl)->num_buckets *= 2U; \ + (tbl)->log2_num_buckets++; \ + (tbl)->buckets = _he_new_buckets; \ + (tbl)->ineff_expands = ((tbl)->nonideal_items > ((tbl)->num_items >> 1)) ? \ + ((tbl)->ineff_expands+1U) : 0U; \ + if ((tbl)->ineff_expands > 1U) { \ + (tbl)->noexpand = 1; \ + uthash_noexpand_fyi(tbl); \ } \ uthash_expand_fyi(tbl); \ -} while(0) + } \ +} while (0) /* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ @@ -739,85 +964,82 @@ do { unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ if (head != NULL) { \ - _hs_insize = 1; \ - _hs_looping = 1; \ - _hs_list = &((head)->hh); \ - while (_hs_looping != 0U) { \ - _hs_p = _hs_list; \ - _hs_list = NULL; \ - _hs_tail = NULL; \ - _hs_nmerges = 0; \ - while (_hs_p != NULL) { \ - _hs_nmerges++; \ - _hs_q = _hs_p; \ - _hs_psize = 0; \ - for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \ - _hs_psize++; \ - _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ? \ - ((void*)((char*)(_hs_q->next) + \ - (head)->hh.tbl->hho)) : NULL); \ - if (! (_hs_q) ) { break; } \ - } \ - _hs_qsize = _hs_insize; \ - while ((_hs_psize > 0U) || ((_hs_qsize > 0U) && (_hs_q != NULL))) {\ - if (_hs_psize == 0U) { \ - _hs_e = _hs_q; \ - _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ? \ - ((void*)((char*)(_hs_q->next) + \ - (head)->hh.tbl->hho)) : NULL); \ - _hs_qsize--; \ - } else if ( (_hs_qsize == 0U) || (_hs_q == NULL) ) { \ - _hs_e = _hs_p; \ - if (_hs_p != NULL){ \ - _hs_p = (UT_hash_handle*)((_hs_p->next != NULL) ? \ - ((void*)((char*)(_hs_p->next) + \ - (head)->hh.tbl->hho)) : NULL); \ - } \ - _hs_psize--; \ - } else if (( \ - cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \ - DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \ - ) <= 0) { \ - _hs_e = _hs_p; \ - if (_hs_p != NULL){ \ - _hs_p = (UT_hash_handle*)((_hs_p->next != NULL) ? \ - ((void*)((char*)(_hs_p->next) + \ - (head)->hh.tbl->hho)) : NULL); \ - } \ - _hs_psize--; \ - } else { \ - _hs_e = _hs_q; \ - _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ? \ - ((void*)((char*)(_hs_q->next) + \ - (head)->hh.tbl->hho)) : NULL); \ - _hs_qsize--; \ - } \ - if ( _hs_tail != NULL ) { \ - _hs_tail->next = ((_hs_e != NULL) ? \ - ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \ - } else { \ - _hs_list = _hs_e; \ - } \ - if (_hs_e != NULL) { \ - _hs_e->prev = ((_hs_tail != NULL) ? \ - ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \ - } \ - _hs_tail = _hs_e; \ - } \ - _hs_p = _hs_q; \ + _hs_insize = 1; \ + _hs_looping = 1; \ + _hs_list = &((head)->hh); \ + while (_hs_looping != 0U) { \ + _hs_p = _hs_list; \ + _hs_list = NULL; \ + _hs_tail = NULL; \ + _hs_nmerges = 0; \ + while (_hs_p != NULL) { \ + _hs_nmerges++; \ + _hs_q = _hs_p; \ + _hs_psize = 0; \ + for (_hs_i = 0; _hs_i < _hs_insize; ++_hs_i) { \ + _hs_psize++; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + if (_hs_q == NULL) { \ + break; \ } \ - if (_hs_tail != NULL){ \ - _hs_tail->next = NULL; \ + } \ + _hs_qsize = _hs_insize; \ + while ((_hs_psize != 0U) || ((_hs_qsize != 0U) && (_hs_q != NULL))) { \ + if (_hs_psize == 0U) { \ + _hs_e = _hs_q; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + _hs_qsize--; \ + } else if ((_hs_qsize == 0U) || (_hs_q == NULL)) { \ + _hs_e = _hs_p; \ + if (_hs_p != NULL) { \ + _hs_p = ((_hs_p->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ + } \ + _hs_psize--; \ + } else if ((cmpfcn( \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_p)), \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_q)) \ + )) <= 0) { \ + _hs_e = _hs_p; \ + if (_hs_p != NULL) { \ + _hs_p = ((_hs_p->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ + } \ + _hs_psize--; \ + } else { \ + _hs_e = _hs_q; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + _hs_qsize--; \ + } \ + if ( _hs_tail != NULL ) { \ + _hs_tail->next = ((_hs_e != NULL) ? \ + ELMT_FROM_HH((head)->hh.tbl, _hs_e) : NULL); \ + } else { \ + _hs_list = _hs_e; \ } \ - if ( _hs_nmerges <= 1U ) { \ - _hs_looping=0; \ - (head)->hh.tbl->tail = _hs_tail; \ - DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ + if (_hs_e != NULL) { \ + _hs_e->prev = ((_hs_tail != NULL) ? \ + ELMT_FROM_HH((head)->hh.tbl, _hs_tail) : NULL); \ } \ - _hs_insize *= 2U; \ + _hs_tail = _hs_e; \ + } \ + _hs_p = _hs_q; \ + } \ + if (_hs_tail != NULL) { \ + _hs_tail->next = NULL; \ + } \ + if (_hs_nmerges <= 1U) { \ + _hs_looping = 0; \ + (head)->hh.tbl->tail = _hs_tail; \ + DECLTYPE_ASSIGN(head, ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ } \ - HASH_FSCK(hh,head); \ - } \ + _hs_insize *= 2U; \ + } \ + HASH_FSCK(hh, head, "HASH_SRT"); \ + } \ } while (0) /* This function selects items from one hash into another hash. @@ -828,54 +1050,74 @@ do { #define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ do { \ unsigned _src_bkt, _dst_bkt; \ - void *_last_elt=NULL, *_elt; \ + void *_last_elt = NULL, *_elt; \ UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ - if (src != NULL) { \ - for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ - for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ - _src_hh != NULL; \ - _src_hh = _src_hh->hh_next) { \ - _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ - if (cond(_elt)) { \ - _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ - _dst_hh->key = _src_hh->key; \ - _dst_hh->keylen = _src_hh->keylen; \ - _dst_hh->hashv = _src_hh->hashv; \ - _dst_hh->prev = _last_elt; \ - _dst_hh->next = NULL; \ - if (_last_elt_hh != NULL) { _last_elt_hh->next = _elt; } \ - if (dst == NULL) { \ - DECLTYPE_ASSIGN(dst,_elt); \ - HASH_MAKE_TABLE(hh_dst,dst); \ - } else { \ - _dst_hh->tbl = (dst)->hh_dst.tbl; \ - } \ - HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ - HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \ - (dst)->hh_dst.tbl->num_items++; \ - _last_elt = _elt; \ - _last_elt_hh = _dst_hh; \ + if ((src) != NULL) { \ + for (_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ + for (_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ + _src_hh != NULL; \ + _src_hh = _src_hh->hh_next) { \ + _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ + if (cond(_elt)) { \ + IF_HASH_NONFATAL_OOM( int _hs_oomed = 0; ) \ + _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ + _dst_hh->key = _src_hh->key; \ + _dst_hh->keylen = _src_hh->keylen; \ + _dst_hh->hashv = _src_hh->hashv; \ + _dst_hh->prev = _last_elt; \ + _dst_hh->next = NULL; \ + if (_last_elt_hh != NULL) { \ + _last_elt_hh->next = _elt; \ } \ + if ((dst) == NULL) { \ + DECLTYPE_ASSIGN(dst, _elt); \ + HASH_MAKE_TABLE(hh_dst, dst, _hs_oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (_hs_oomed) { \ + uthash_nonfatal_oom(_elt); \ + (dst) = NULL; \ + continue; \ + } \ + ) \ + } else { \ + _dst_hh->tbl = (dst)->hh_dst.tbl; \ + } \ + HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ + HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt], hh_dst, _dst_hh, _hs_oomed); \ + (dst)->hh_dst.tbl->num_items++; \ + IF_HASH_NONFATAL_OOM( \ + if (_hs_oomed) { \ + HASH_ROLLBACK_BKT(hh_dst, dst, _dst_hh); \ + HASH_DELETE_HH(hh_dst, dst, _dst_hh); \ + _dst_hh->tbl = NULL; \ + uthash_nonfatal_oom(_elt); \ + continue; \ + } \ + ) \ + HASH_BLOOM_ADD(_dst_hh->tbl, _dst_hh->hashv); \ + _last_elt = _elt; \ + _last_elt_hh = _dst_hh; \ + } \ } \ } \ } \ - HASH_FSCK(hh_dst,dst); \ + HASH_FSCK(hh_dst, dst, "HASH_SELECT"); \ } while (0) #define HASH_CLEAR(hh,head) \ do { \ - if (head != NULL) { \ + if ((head) != NULL) { \ + HASH_BLOOM_FREE((head)->hh.tbl); \ uthash_free((head)->hh.tbl->buckets, \ (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ - HASH_BLOOM_FREE((head)->hh.tbl); \ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ - (head)=NULL; \ + (head) = NULL; \ } \ -} while(0) +} while (0) #define HASH_OVERHEAD(hh,head) \ - ((head != NULL) ? ( \ + (((head) != NULL) ? ( \ (size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ sizeof(UT_hash_table) + \ @@ -963,4 +1205,4 @@ typedef struct UT_hash_handle { unsigned hashv; /* result of hash-fcn(key) */ } UT_hash_handle; -#endif /* UTHASH_H */ +#endif /* UTHASH_H */ \ No newline at end of file diff --git a/include/otfcc/font.h b/include/otfcc/font.h index 089ad017..a7d58316 100644 --- a/include/otfcc/font.h +++ b/include/otfcc/font.h @@ -8,6 +8,8 @@ typedef struct _caryll_font otfcc_Font; #include "otfcc/glyph-order.h" +#include "otfcc/table/fvar.h" + #include "otfcc/table/head.h" #include "otfcc/table/glyf.h" #include "otfcc/table/CFF.h" @@ -25,6 +27,7 @@ typedef struct _caryll_font otfcc_Font; #include "otfcc/table/cvt.h" #include "otfcc/table/fpgm-prep.h" #include "otfcc/table/gasp.h" +#include "otfcc/table/VDMX.h" #include "otfcc/table/LTSH.h" #include "otfcc/table/VORG.h" @@ -45,6 +48,8 @@ typedef enum { FONTTYPE_TTF, FONTTYPE_CFF } otfcc_font_subtype; struct _caryll_font { otfcc_font_subtype subtype; + table_fvar *fvar; + table_head *head; table_hhea *hhea; table_maxp *maxp; @@ -66,6 +71,7 @@ struct _caryll_font { table_fpgm_prep *prep; table_cvt *cvt_; table_gasp *gasp; + table_VDMX *VDMX; table_LTSH *LTSH; diff --git a/include/otfcc/options.h b/include/otfcc/options.h index dc823272..cd458726 100644 --- a/include/otfcc/options.h +++ b/include/otfcc/options.h @@ -28,6 +28,7 @@ typedef struct { bool stub_cmap4; bool decimal_cmap; bool name_glyphs_by_hash; + bool name_glyphs_by_gid; char *glyph_name_prefix; otfcc_ILogger *logger; } otfcc_Options; diff --git a/include/otfcc/primitives.h b/include/otfcc/primitives.h index 8a316dc0..69ae393b 100644 --- a/include/otfcc/primitives.h +++ b/include/otfcc/primitives.h @@ -5,8 +5,11 @@ #include #include -typedef int16_t f2dot14; -typedef int32_t f16dot16; +typedef int16_t f2dot14; // 2.14 Fixed number, representing a value between [-1, 1]. +typedef int32_t f16dot16; // 16.16 Fixed number, usually used by intermediate coordiantes of a font. + // To deal with implicit deltas in GVAR we must be very careful about it. + // Arithmetic operators saturate towards positive or negative infinity. + // Infinity values short circuit expressions. typedef uint16_t glyphid_t; // Glyph index typedef uint16_t glyphclass_t; // Glyph class @@ -30,4 +33,15 @@ int16_t otfcc_to_f2dot14(const double x); double otfcc_from_fixed(const f16dot16 x); f16dot16 otfcc_to_fixed(const double x); +#define f16dot16_precision 16 +#define f16dot16_k (1 << (f16dot16_precision - 1)) +#define f16dot16_infinity ((f16dot16)0x7fffffff) +#define f16dot16_negativeIntinity ((f16dot16)0x80000000) + +f16dot16 otfcc_f1616_add(f16dot16 a, f16dot16 b); +f16dot16 otfcc_f1616_minus(f16dot16 a, f16dot16 b); +f16dot16 otfcc_f1616_multiply(f16dot16 a, f16dot16 b); +f16dot16 otfcc_f1616_divide(f16dot16 a, f16dot16 b); +f16dot16 otfcc_f1616_muldiv(f16dot16 a, f16dot16 b, f16dot16 c); + #endif diff --git a/include/otfcc/table/VDMX.h b/include/otfcc/table/VDMX.h new file mode 100644 index 00000000..0cd1f242 --- /dev/null +++ b/include/otfcc/table/VDMX.h @@ -0,0 +1,36 @@ +#ifndef CARYLL_INCLUDE_TABLE_VDMX_H +#define CARYLL_INCLUDE_TABLE_VDMX_H + +#include "table-common.h" + +typedef struct { + uint16_t yPelHeight; + int16_t yMax; + int16_t yMin; +} vdmx_Record; + +extern caryll_ValElementInterface(vdmx_Record) vdmx_iRecord; +typedef caryll_Vector(vdmx_Record) vdmx_Group; +extern caryll_VectorInterface(vdmx_Group, vdmx_Record) vdmx_iGroup; + +typedef struct { + uint8_t bCharset; + uint8_t xRatio; + uint8_t yStartRatio; + uint8_t yEndRatio; + + vdmx_Group records; +} vdmx_RatioRange; + +extern caryll_ElementInterface(vdmx_RatioRange) vdmx_iRatioRange; +typedef caryll_Vector(vdmx_RatioRange) vdmx_RatioRagneList; +extern caryll_VectorInterface(vdmx_RatioRagneList, vdmx_RatioRange) vdmx_iRatioRangeList; + +typedef struct { + uint16_t version; + vdmx_RatioRagneList ratios; +} table_VDMX; + +extern caryll_RefElementInterface(table_VDMX) table_iVDMX; + +#endif diff --git a/include/otfcc/table/fvar.h b/include/otfcc/table/fvar.h new file mode 100644 index 00000000..767496df --- /dev/null +++ b/include/otfcc/table/fvar.h @@ -0,0 +1,40 @@ +#ifndef CARYLL_INCLUDE_TABLE_FVAR_H +#define CARYLL_INCLUDE_TABLE_FVAR_H + +#include "table-common.h" +#include "otfcc/vf/vf.h" + +// vf_Axis and vf_Axes are defined in vf/vf.h +// fvar_Instance is defined below +typedef struct { + uint16_t subfamilyNameID; + uint16_t flags; + VV coordinates; + uint16_t postScriptNameID; +} fvar_Instance; +extern caryll_ElementInterface(fvar_Instance) fvar_iInstance; +typedef caryll_Vector(fvar_Instance) fvar_InstanceList; +extern caryll_VectorInterface(fvar_InstanceList, fvar_Instance) fvar_iInstanceList; + +typedef struct { + sds name; + vq_Region *region; + UT_hash_handle hh; +} fvar_Master; + +typedef struct { + uint16_t majorVersion; + uint16_t minorVersion; + vf_Axes axes; + fvar_InstanceList instances; + fvar_Master *masters; +} table_fvar; + +extern caryll_ElementInterfaceOf(table_fvar) { + caryll_RT(table_fvar); + const vq_Region *(*registerRegion)(table_fvar * fvar, MOVE vq_Region * region); + const fvar_Master *(*findMasterByRegion)(const table_fvar *fvar, const vq_Region *region); +} +table_iFvar; + +#endif diff --git a/include/otfcc/table/glyf.h b/include/otfcc/table/glyf.h index f115e10c..c0c043d2 100644 --- a/include/otfcc/table/glyf.h +++ b/include/otfcc/table/glyf.h @@ -4,6 +4,7 @@ #include "table-common.h" #include "head.h" #include "maxp.h" +#include "fvar.h" enum GlyphType { SIMPLE, COMPOSITE }; @@ -52,21 +53,23 @@ typedef enum { } RefAnchorStatus; typedef struct { + //// NOTE: this part and below looks like a glyf_Point + VQ x; + VQ y; + // flags + bool roundToGrid; + bool useMyMetrics; + // the glyph being referenced otfcc_GlyphHandle glyph; // transformation term scale_t a; scale_t b; scale_t c; scale_t d; - // position term + // Anchorness term RefAnchorStatus isAnchored; shapeid_t inner; shapeid_t outer; - VQ x; - VQ y; - // flags - bool roundToGrid; - bool useMyMetrics; } glyf_ComponentReference; extern caryll_ValElementInterface(glyf_ComponentReference) glyf_iComponentReference; typedef caryll_Vector(glyf_ComponentReference) glyf_ReferenceList; @@ -112,6 +115,7 @@ typedef struct { // CID FDSelect otfcc_FDHandle fdSelect; + glyphid_t cid; // Subset CID fonts may need this to represent the original CID entry // Stats glyf_GlyphStat stat; diff --git a/include/otfcc/table/hhea.h b/include/otfcc/table/hhea.h index 6388624c..d9d94b86 100644 --- a/include/otfcc/table/hhea.h +++ b/include/otfcc/table/hhea.h @@ -9,7 +9,7 @@ typedef struct { int16_t ascender; int16_t descender; int16_t lineGap; - uint16_t advanceWithMax; + uint16_t advanceWidthMax; int16_t minLeftSideBearing; int16_t minRightSideBearing; int16_t xMaxExtent; diff --git a/include/otfcc/vf/axis.h b/include/otfcc/vf/axis.h new file mode 100644 index 00000000..54d0d7cd --- /dev/null +++ b/include/otfcc/vf/axis.h @@ -0,0 +1,21 @@ +#ifndef CARYLL_VF_AXIS_H +#define CARYLL_VF_AXIS_H + +#include "caryll/element.h" +#include "caryll/vector.h" +#include "otfcc/primitives.h" + +typedef struct { + uint32_t tag; + pos_t minValue; + pos_t defaultValue; + pos_t maxValue; + uint16_t flags; + uint16_t axisNameID; +} vf_Axis; + +extern caryll_ValElementInterface(vf_Axis) vf_iAxis; +typedef caryll_Vector(vf_Axis) vf_Axes; +extern caryll_VectorInterface(vf_Axes, vf_Axis) vf_iAxes; + +#endif diff --git a/include/otfcc/vf/region.h b/include/otfcc/vf/region.h new file mode 100644 index 00000000..b6dbd344 --- /dev/null +++ b/include/otfcc/vf/region.h @@ -0,0 +1,34 @@ +#ifndef CARYLL_VF_REGION_H +#define CARYLL_VF_REGION_H + +#include "caryll/element.h" +#include "caryll/vector.h" +#include "otfcc/primitives.h" + +#include "vv.h" + +typedef struct { + pos_t start; + pos_t peak; + pos_t end; +} vq_AxisSpan; + +bool vq_AxisSpanIsOne(const vq_AxisSpan *a); + +typedef struct { + shapeid_t dimensions; + vq_AxisSpan spans[]; +} vq_Region; + +vq_Region *vq_createRegion(shapeid_t dimensions); +void vq_deleteRegion(MOVE vq_Region *region); +vq_Region *vq_copyRegion(const vq_Region *region); + +int vq_compareRegion(const vq_Region *a, const vq_Region *b); +pos_t vq_regionGetWeight(const vq_Region *r, const VV *v); +void vq_showRegion(const vq_Region *r); + +// function macros +#define VQ_REGION_SIZE(n) (sizeof(vq_Region) + sizeof(vq_AxisSpan) * (n)) + +#endif diff --git a/include/otfcc/vf/vf.h b/include/otfcc/vf/vf.h index 4e1712ba..c30928ab 100644 --- a/include/otfcc/vf/vf.h +++ b/include/otfcc/vf/vf.h @@ -9,5 +9,6 @@ // part as a Ket. #include "otfcc/vf/vq.h" +#include "otfcc/vf/axis.h" #endif diff --git a/include/otfcc/vf/vq.h b/include/otfcc/vf/vq.h index 8d296a89..b25c4c19 100644 --- a/include/otfcc/vf/vq.h +++ b/include/otfcc/vf/vq.h @@ -7,29 +7,7 @@ #include "otfcc/primitives.h" #include "otfcc/handle.h" -extern caryll_ValElementInterface(pos_t) vq_iPosT; -typedef caryll_Vector(pos_t) VV; -extern caryll_VectorInterfaceTypeName(VV) { - caryll_VectorInterfaceTrait(VV, pos_t); - // Monoid instances - VV (*neutral)(tableid_t dimensions); -} -iVV; -// extern caryll_VectorInterface(VV, pos_t) iVV; - -typedef struct { - pos_t start; - pos_t peak; - pos_t end; -} vq_AxisSpan; -extern caryll_ElementInterface(vq_AxisSpan) vq_iAxisSpan; -typedef caryll_Vector(vq_AxisSpan) vq_Region; -extern caryll_VectorInterfaceTypeName(vq_Region) { - caryll_VectorInterfaceTrait(vq_Region, vq_AxisSpan); - caryll_Ord(vq_Region); - pos_t (*getWeight)(const vq_Region *r, const VV *vv); -} -vq_iRegion; +#include "region.h" typedef enum { VQ_STILL = 0, VQ_DELTA = 1 } VQSegType; typedef struct { @@ -38,7 +16,8 @@ typedef struct { pos_t still; struct { pos_t quantity; - vq_Region region; + bool touched; + const vq_Region *region; // non-owning : they are in FVAR } delta; } val; } vq_Segment; @@ -48,7 +27,7 @@ extern caryll_ElementInterfaceOf(vq_Segment) { caryll_Show(vq_Segment); caryll_Ord(vq_Segment); vq_Segment (*createStill)(pos_t x); - vq_Segment (*createDelta)(pos_t delta, MOVE vq_Region region); + vq_Segment (*createDelta)(pos_t delta, vq_Region * region); } vq_iSegment; typedef caryll_Vector(vq_Segment) vq_SegList; diff --git a/include/otfcc/vf/vv.h b/include/otfcc/vf/vv.h new file mode 100644 index 00000000..68bfc27f --- /dev/null +++ b/include/otfcc/vf/vv.h @@ -0,0 +1,18 @@ +#ifndef CARYLL_VF_VV_H +#define CARYLL_VF_VV_H + +#include "caryll/element.h" +#include "caryll/vector.h" +#include "otfcc/primitives.h" + +extern caryll_ValElementInterface(pos_t) vq_iPosT; +typedef caryll_Vector(pos_t) VV; +extern caryll_VectorInterfaceTypeName(VV) { + caryll_VectorInterfaceTrait(VV, pos_t); + // Monoid instances + VV (*neutral)(tableid_t dimensions); +} +iVV; +// extern caryll_VectorInterface(VV, pos_t) iVV; + +#endif diff --git a/lib/bk/bkgraph.c b/lib/bk/bkgraph.c index a16a89b2..0bb84bb5 100644 --- a/lib/bk/bkgraph.c +++ b/lib/bk/bkgraph.c @@ -204,8 +204,8 @@ static uint32_t getoffset(size_t *offsets, bk_Block *ref, bk_Block *target, uint size_t offtgt = offsets[target->_index]; /* if (offtgt < offref || (offtgt - offref) >> bits) { - fprintf(stderr, "[otfcc-fea] Warning : Unable to fit offset %d into %d bits.\n", (int32_t)(offtgt - offref), - bits); + fprintf(stderr, "[otfcc-fea] Warning : Unable to fit offset %d into %d bits.\n", + (int32_t)(offtgt - offref), bits); } */ return (uint32_t)(offtgt - offref); @@ -219,7 +219,9 @@ static void escalate_sppointers(bk_Block *b, bk_Graph *f, uint32_t *order, uint3 if (!b) return; for (uint32_t j = 0; j < b->length; j++) { bk_Cell *cell = &(b->cells[j]); - if (bk_cellIsPointer(cell) && cell->p && cell->t >= sp16) { escalate_sppointers(cell->p, f, order, depth); } + if (bk_cellIsPointer(cell) && cell->p && cell->t >= sp16) { + escalate_sppointers(cell->p, f, order, depth); + } } b->_depth = depth; *order += 1; @@ -356,7 +358,9 @@ caryll_Buffer *bk_build_Graph(bk_Graph *f) { } } for (uint32_t j = 0; j < f->length; j++) { - if (f->entries[j].block->_visitstate == VISIT_BLACK) { otfcc_build_bkblock(buf, f->entries[j].block, offsets); } + if (f->entries[j].block->_visitstate == VISIT_BLACK) { + otfcc_build_bkblock(buf, f->entries[j].block, offsets); + } } FREE(offsets); return buf; @@ -398,3 +402,10 @@ caryll_Buffer *bk_build_Block(/*MOVE*/ bk_Block *root) { bk_delete_Graph(f); return buf; } +caryll_Buffer *bk_build_Block_noMinimize(/*MOVE*/ bk_Block *root) { + bk_Graph *f = bk_newGraphFromRootBlock(root); + bk_untangleGraph(f); + caryll_Buffer *buf = bk_build_Graph(f); + bk_delete_Graph(f); + return buf; +} diff --git a/lib/bk/bkgraph.h b/lib/bk/bkgraph.h index 70a1d141..6d48054b 100644 --- a/lib/bk/bkgraph.h +++ b/lib/bk/bkgraph.h @@ -29,6 +29,7 @@ void bk_minimizeGraph(/*BORROW*/ bk_Graph *f); void bk_untangleGraph(/*BORROW*/ bk_Graph *f); caryll_Buffer *bk_build_Graph(/*BORROW*/ bk_Graph *f); caryll_Buffer *bk_build_Block(/*MOVE*/ bk_Block *root); +caryll_Buffer *bk_build_Block_noMinimize(/*MOVE*/ bk_Block *root); size_t bk_estimateSizeOfGraph(bk_Graph *f); #endif diff --git a/lib/json-reader/json-reader.c b/lib/json-reader/json-reader.c index d70ff280..db9519cd 100644 --- a/lib/json-reader/json-reader.c +++ b/lib/json-reader/json-reader.c @@ -50,7 +50,8 @@ static void orderGlyphs(otfcc_GlyphOrder *go) { } } -static void escalateGlyphOrderByName(otfcc_GlyphOrder *go, sds name, uint8_t orderType, uint32_t orderEntry) { +static void escalateGlyphOrderByName(otfcc_GlyphOrder *go, sds name, uint8_t orderType, + uint32_t orderEntry) { otfcc_GlyphOrderEntry *s = NULL; HASH_FIND(hhName, go->byName, name, sdslen(name), s); if (s && s->orderType > orderType) { @@ -61,7 +62,8 @@ static void escalateGlyphOrderByName(otfcc_GlyphOrder *go, sds name, uint8_t ord static void placeOrderEntriesFromGlyf(json_value *table, otfcc_GlyphOrder *go) { for (uint32_t j = 0; j < table->u.object.length; j++) { - sds gname = sdsnewlen(table->u.object.values[j].name, table->u.object.values[j].name_length); + sds gname = + sdsnewlen(table->u.object.values[j].name, table->u.object.values[j].name_length); if (strcmp(gname, ".notdef") == 0) { setOrderByName(go, gname, ORD_NOTDEF, 0); } else if (strcmp(gname, ".null") == 0) { @@ -73,7 +75,8 @@ static void placeOrderEntriesFromGlyf(json_value *table, otfcc_GlyphOrder *go) { } static void placeOrderEntriesFromCmap(json_value *table, otfcc_GlyphOrder *go) { for (uint32_t j = 0; j < table->u.object.length; j++) { - sds unicodeStr = sdsnewlen(table->u.object.values[j].name, table->u.object.values[j].name_length); + sds unicodeStr = + sdsnewlen(table->u.object.values[j].name, table->u.object.values[j].name_length); json_value *item = table->u.object.values[j].value; int32_t unicode; if (sdslen(unicodeStr) > 2 && unicodeStr[0] == 'U' && unicodeStr[1] == '+') { @@ -82,7 +85,8 @@ static void placeOrderEntriesFromCmap(json_value *table, otfcc_GlyphOrder *go) { unicode = atoi(unicodeStr); } sdsfree(unicodeStr); - if (item->type == json_string && unicode > 0 && unicode <= 0x10FFFF) { // a valid unicode codepoint + if (item->type == json_string && unicode > 0 && + unicode <= 0x10FFFF) { // a valid unicode codepoint sds gname = sdsnewlen(item->u.string.ptr, item->u.string.length); escalateGlyphOrderByName(go, gname, ORD_CMAP, unicode); sdsfree(gname); @@ -109,7 +113,9 @@ static otfcc_GlyphOrder *parseGlyphOrder(const json_value *root, const otfcc_Opt if ((table = json_obj_get_type(root, "glyf", json_object))) { placeOrderEntriesFromGlyf(table, go); - if ((table = json_obj_get_type(root, "cmap", json_object))) { placeOrderEntriesFromCmap(table, go); } + if ((table = json_obj_get_type(root, "cmap", json_object))) { + placeOrderEntriesFromCmap(table, go); + } if ((table = json_obj_get_type(root, "glyph_order", json_array))) { bool ignoreGlyphOrder = options->ignore_glyph_order; if (ignoreGlyphOrder && !!json_obj_get_type(root, "SVG_", json_array)) { @@ -144,6 +150,7 @@ static otfcc_Font *readJson(void *_root, uint32_t index, const otfcc_Options *op font->cvt_ = otfcc_parseCvt(root, options, "cvt_"); font->gasp = otfcc_parseGasp(root, options); } + font->VDMX = otfcc_parseVDMX(root, options); font->vhea = otfcc_parseVhea(root, options); if (font->glyf) { font->GSUB = otfcc_parseOtl(root, options, "GSUB"); diff --git a/lib/json-writer/json-writer.c b/lib/json-writer/json-writer.c index ef7d57a9..81e3e7be 100644 --- a/lib/json-writer/json-writer.c +++ b/lib/json-writer/json-writer.c @@ -5,6 +5,7 @@ static void *serializeToJson(otfcc_Font *font, const otfcc_Options *options) { json_value *root = json_object_new(48); if (!root) return NULL; + otfcc_dumpFvar(font->fvar, root, options); otfcc_dumpHead(font->head, root, options); otfcc_dumpHhea(font->hhea, root, options); otfcc_dumpMaxp(font->maxp, root, options); @@ -14,16 +15,21 @@ static void *serializeToJson(otfcc_Font *font, const otfcc_Options *options) { otfcc_dumpName(font->name, root, options); otfcc_dumpCmap(font->cmap, root, options); otfcc_dumpCFF(font->CFF_, root, options); - otfcc_dumpGlyf(font->glyf, root, options, // - !!(font->vhea) && !!(font->vmtx), // whether export vertical metrics - font->CFF_ && font->CFF_->isCID // whether export FDSelect - ); + + GlyfIOContext ctx = {.locaIsLong = font->head->indexToLocFormat, + .numGlyphs = font->maxp->numGlyphs, + .nPhantomPoints = 4, + .hasVerticalMetrics = !!(font->vhea) && !!(font->vmtx), + .exportFDSelect = font->CFF_ && font->CFF_->isCID, + .fvar = font->fvar}; + otfcc_dumpGlyf(font->glyf, root, options, &ctx); if (!options->ignore_hints) { table_dumpTableFpgmPrep(font->fpgm, root, options, "fpgm"); table_dumpTableFpgmPrep(font->prep, root, options, "prep"); otfcc_dumpCvt(font->cvt_, root, options, "cvt_"); otfcc_dumpGasp(font->gasp, root, options); } + otfcc_dumpVDMX(font->VDMX, root, options); otfcc_dumpOtl(font->GSUB, root, options, "GSUB"); otfcc_dumpOtl(font->GPOS, root, options, "GPOS"); otfcc_dumpGDEF(font->GDEF, root, options); diff --git a/lib/otf-reader/otf-reader.c b/lib/otf-reader/otf-reader.c index b378851a..8d29d391 100644 --- a/lib/otf-reader/otf-reader.c +++ b/lib/otf-reader/otf-reader.c @@ -20,6 +20,7 @@ static otfcc_Font *readOtf(void *_sfnt, uint32_t index, const otfcc_Options *opt otfcc_Font *font = otfcc_iFont.create(); otfcc_Packet packet = sfnt->packets[index]; font->subtype = decideFontSubtypeOTF(sfnt, index); + font->fvar = otfcc_readFvar(packet, options); font->head = otfcc_readHead(packet, options); font->maxp = otfcc_readMaxp(packet, options); font->name = otfcc_readName(packet, options); @@ -35,8 +36,15 @@ static otfcc_Font *readOtf(void *_sfnt, uint32_t index, const otfcc_Options *opt font->prep = otfcc_readFpgmPrep(packet, options, 'prep'); font->cvt_ = otfcc_readCvt(packet, options, 'cvt '); font->gasp = otfcc_readGasp(packet, options); + font->VDMX = otfcc_readVDMX(packet, options); font->LTSH = otfcc_readLTSH(packet, options); - font->glyf = otfcc_readGlyf(packet, options, font->head, font->maxp); + + GlyfIOContext ctx = {.locaIsLong = font->head->indexToLocFormat, + .numGlyphs = font->maxp->numGlyphs, + .nPhantomPoints = 4, // Since MS rasterizer v1.7, + // it would always add 4 phantom points + .fvar = font->fvar}; + font->glyf = otfcc_readGlyf(packet, options, &ctx); } else { table_CFFAndGlyf cffpr = otfcc_readCFFAndGlyfTables(packet, options, font->head); font->CFF_ = cffpr.meta; diff --git a/lib/otf-reader/unconsolidate.c b/lib/otf-reader/unconsolidate.c index 8c6f9e8a..df05bd9c 100644 --- a/lib/otf-reader/unconsolidate.c +++ b/lib/otf-reader/unconsolidate.c @@ -3,7 +3,9 @@ #include "support/aglfn/aglfn.h" #include "support/sha1/sha1.h" -typedef struct { uint8_t hash[SHA1_BLOCK_SIZE]; } GlyphHash; +typedef struct { + uint8_t hash[SHA1_BLOCK_SIZE]; +} GlyphHash; static void hashVQS(caryll_Buffer *buf, vq_Segment s) { bufwrite8(buf, s.type); switch (s.type) { @@ -12,9 +14,9 @@ static void hashVQS(caryll_Buffer *buf, vq_Segment s) { break; case VQ_DELTA: bufwrite32b(buf, otfcc_to_fixed(s.val.delta.quantity)); - bufwrite32b(buf, (uint32_t)s.val.delta.region.length); - for (size_t j = 0; j < s.val.delta.region.length; j++) { - vq_AxisSpan *span = &s.val.delta.region.items[j]; + bufwrite32b(buf, (uint32_t)s.val.delta.region->dimensions); + for (size_t j = 0; j < s.val.delta.region->dimensions; j++) { + const vq_AxisSpan *span = &s.val.delta.region->spans[j]; bufwrite32b(buf, otfcc_to_f2dot14(span->start)); bufwrite32b(buf, otfcc_to_f2dot14(span->peak)); bufwrite32b(buf, otfcc_to_f2dot14(span->end)); @@ -134,8 +136,7 @@ GlyphHash nameGlyphByHash(glyf_Glyph *g, table_glyf *glyf) { // (Separate?) static otfcc_GlyphOrder *createGlyphOrder(otfcc_Font *font, const otfcc_Options *options) { otfcc_GlyphOrder *glyph_order = GlyphOrder.create(); - otfcc_GlyphOrder *aglfn = GlyphOrder.create(); - aglfn_setupNames(aglfn); + glyphid_t numGlyphs = font->glyf->length; sds prefix; if (options->glyph_name_prefix) { @@ -177,8 +178,9 @@ static otfcc_GlyphOrder *createGlyphOrder(otfcc_Font *font, const otfcc_Options if (g->name) sdsfree(g->name); g->name = sdsdup(sharedName); } - } else if (options->ignore_glyph_order) { // ignore built-in names - // pass + } else if (options->ignore_glyph_order || options->name_glyphs_by_gid) { + // ignore built-in names + // pass } else if (g->name) { sds gname = sdscatprintf(sdsempty(), "%s%s", prefix, g->name); sds sharedName = GlyphOrder.setByGID(glyph_order, j, gname); @@ -188,7 +190,8 @@ static otfcc_GlyphOrder *createGlyphOrder(otfcc_Font *font, const otfcc_Options } // pass 2: Map to `post` names - if (font->post != NULL && font->post->post_name_map != NULL && !options->ignore_glyph_order) { + if (font->post != NULL && font->post->post_name_map != NULL && !options->ignore_glyph_order && + !options->name_glyphs_by_gid) { otfcc_GlyphOrderEntry *s, *tmp; HASH_ITER(hhID, font->post->post_name_map->byGID, s, tmp) { sds gname = sdscatprintf(sdsempty(), "%s%s", prefix, s->name); @@ -197,7 +200,10 @@ static otfcc_GlyphOrder *createGlyphOrder(otfcc_Font *font, const otfcc_Options } // pass 3: Map to AGLFN & Unicode - if (font->cmap != NULL) { + if (font->cmap && !options->name_glyphs_by_gid) { + otfcc_GlyphOrder *aglfn = GlyphOrder.create(); + aglfn_setupNames(aglfn); + cmap_Entry *s; foreach_hash(s, font->cmap->unicodes) if (s->glyph.index > 0) { sds name = NULL; @@ -211,6 +217,8 @@ static otfcc_GlyphOrder *createGlyphOrder(otfcc_Font *font, const otfcc_Options } GlyphOrder.setByGID(glyph_order, s->glyph.index, name); } + + GlyphOrder.free(aglfn); } // pass 4 : Map to GID @@ -232,7 +240,6 @@ static otfcc_GlyphOrder *createGlyphOrder(otfcc_Font *font, const otfcc_Options GlyphOrder.setByGID(glyph_order, j, name); } - GlyphOrder.free(aglfn); sdsfree(prefix); return glyph_order; } diff --git a/lib/otf-writer/otf-writer.c b/lib/otf-writer/otf-writer.c index ad24d6e0..5e5ffb24 100644 --- a/lib/otf-writer/otf-writer.c +++ b/lib/otf-writer/otf-writer.c @@ -8,7 +8,8 @@ static void *serializeToOTF(otfcc_Font *font, const otfcc_Options *options) { // do stat before serialize otfcc_statFont(font, options); - otfcc_SFNTBuilder *builder = otfcc_newSFNTBuilder(font->subtype == FONTTYPE_CFF ? 'OTTO' : 0x00010000, options); + otfcc_SFNTBuilder *builder = + otfcc_newSFNTBuilder(font->subtype == FONTTYPE_CFF ? 'OTTO' : 0x00010000, options); // Outline data if (font->subtype == FONTTYPE_TTF) { table_GlyfAndLocaBuffers pair = otfcc_buildGlyf(font->glyf, font->head, options); @@ -24,38 +25,58 @@ static void *serializeToOTF(otfcc_Font *font, const otfcc_Options *options) { otfcc_SFNTBuilder_pushTable(builder, 'OS/2', otfcc_buildOS_2(font->OS_2, options)); otfcc_SFNTBuilder_pushTable(builder, 'maxp', otfcc_buildMaxp(font->maxp, options)); otfcc_SFNTBuilder_pushTable(builder, 'name', otfcc_buildName(font->name, options)); - otfcc_SFNTBuilder_pushTable(builder, 'post', otfcc_buildPost(font->post, font->glyph_order, options)); + otfcc_SFNTBuilder_pushTable(builder, 'post', + otfcc_buildPost(font->post, font->glyph_order, options)); otfcc_SFNTBuilder_pushTable(builder, 'cmap', otfcc_buildCmap(font->cmap, options)); - if (font->gasp) otfcc_SFNTBuilder_pushTable(builder, 'gasp', otfcc_buildGasp(font->gasp, options)); + if (font->gasp) + otfcc_SFNTBuilder_pushTable(builder, 'gasp', otfcc_buildGasp(font->gasp, options)); if (font->subtype == FONTTYPE_TTF) { - if (font->fpgm) otfcc_SFNTBuilder_pushTable(builder, 'fpgm', otfcc_buildFpgmPrep(font->fpgm, options)); - if (font->prep) otfcc_SFNTBuilder_pushTable(builder, 'prep', otfcc_buildFpgmPrep(font->prep, options)); - if (font->cvt_) otfcc_SFNTBuilder_pushTable(builder, 'cvt ', otfcc_buildCvt(font->cvt_, options)); - if (font->LTSH) otfcc_SFNTBuilder_pushTable(builder, 'LTSH', otfcc_buildLTSH(font->LTSH, options)); + if (font->fpgm) + otfcc_SFNTBuilder_pushTable(builder, 'fpgm', otfcc_buildFpgmPrep(font->fpgm, options)); + if (font->prep) + otfcc_SFNTBuilder_pushTable(builder, 'prep', otfcc_buildFpgmPrep(font->prep, options)); + if (font->cvt_) + otfcc_SFNTBuilder_pushTable(builder, 'cvt ', otfcc_buildCvt(font->cvt_, options)); + if (font->LTSH) + otfcc_SFNTBuilder_pushTable(builder, 'LTSH', otfcc_buildLTSH(font->LTSH, options)); + if (font->VDMX) + otfcc_SFNTBuilder_pushTable(builder, 'VDMX', otfcc_buildVDMX(font->VDMX, options)); } if (font->hhea && font->maxp && font->hmtx) { uint16_t hmtx_counta = font->hhea->numberOfMetrics; uint16_t hmtx_countk = font->maxp->numGlyphs - font->hhea->numberOfMetrics; - otfcc_SFNTBuilder_pushTable(builder, 'hmtx', otfcc_buildHmtx(font->hmtx, hmtx_counta, hmtx_countk, options)); + otfcc_SFNTBuilder_pushTable(builder, 'hmtx', + otfcc_buildHmtx(font->hmtx, hmtx_counta, hmtx_countk, options)); } - if (font->vhea) otfcc_SFNTBuilder_pushTable(builder, 'vhea', otfcc_buildVhea(font->vhea, options)); + if (font->vhea) + otfcc_SFNTBuilder_pushTable(builder, 'vhea', otfcc_buildVhea(font->vhea, options)); if (font->vhea && font->maxp && font->vmtx) { uint16_t vmtx_counta = font->vhea->numOfLongVerMetrics; uint16_t vmtx_countk = font->maxp->numGlyphs - font->vhea->numOfLongVerMetrics; - otfcc_SFNTBuilder_pushTable(builder, 'vmtx', otfcc_buildVmtx(font->vmtx, vmtx_counta, vmtx_countk, options)); + otfcc_SFNTBuilder_pushTable(builder, 'vmtx', + otfcc_buildVmtx(font->vmtx, vmtx_counta, vmtx_countk, options)); + } + if (font->VORG) { + otfcc_SFNTBuilder_pushTable(builder, 'VORG', otfcc_buildVORG(font->VORG, options)); } - if (font->VORG) { otfcc_SFNTBuilder_pushTable(builder, 'VORG', otfcc_buildVORG(font->VORG, options)); } - if (font->GSUB) otfcc_SFNTBuilder_pushTable(builder, 'GSUB', otfcc_buildOtl(font->GSUB, options, "GSUB")); - if (font->GPOS) otfcc_SFNTBuilder_pushTable(builder, 'GPOS', otfcc_buildOtl(font->GPOS, options, "GPOS")); - if (font->GDEF) otfcc_SFNTBuilder_pushTable(builder, 'GDEF', otfcc_buildGDEF(font->GDEF, options)); - if (font->BASE) otfcc_SFNTBuilder_pushTable(builder, 'BASE', otfcc_buildBASE(font->BASE, options)); + if (font->GSUB) + otfcc_SFNTBuilder_pushTable(builder, 'GSUB', otfcc_buildOtl(font->GSUB, options, "GSUB")); + if (font->GPOS) + otfcc_SFNTBuilder_pushTable(builder, 'GPOS', otfcc_buildOtl(font->GPOS, options, "GPOS")); + if (font->GDEF) + otfcc_SFNTBuilder_pushTable(builder, 'GDEF', otfcc_buildGDEF(font->GDEF, options)); + if (font->BASE) + otfcc_SFNTBuilder_pushTable(builder, 'BASE', otfcc_buildBASE(font->BASE, options)); - if (font->CPAL) otfcc_SFNTBuilder_pushTable(builder, 'CPAL', otfcc_buildCPAL(font->CPAL, options)); - if (font->COLR) otfcc_SFNTBuilder_pushTable(builder, 'COLR', otfcc_buildCOLR(font->COLR, options)); - if (font->SVG_) otfcc_SFNTBuilder_pushTable(builder, 'SVG ', otfcc_buildSVG(font->SVG_, options)); + if (font->CPAL) + otfcc_SFNTBuilder_pushTable(builder, 'CPAL', otfcc_buildCPAL(font->CPAL, options)); + if (font->COLR) + otfcc_SFNTBuilder_pushTable(builder, 'COLR', otfcc_buildCOLR(font->COLR, options)); + if (font->SVG_) + otfcc_SFNTBuilder_pushTable(builder, 'SVG ', otfcc_buildSVG(font->SVG_, options)); if (font->TSI_01) { tsi_BuildTarget target = otfcc_buildTSI(font->TSI_01, options); @@ -68,7 +89,8 @@ static void *serializeToOTF(otfcc_Font *font, const otfcc_Options *options) { otfcc_SFNTBuilder_pushTable(builder, 'TSI3', target.textPart); } if (font->glyf && font->TSI5) { - otfcc_SFNTBuilder_pushTable(builder, 'TSI5', otfcc_buildTSI5(font->TSI5, options, font->glyf->length)); + otfcc_SFNTBuilder_pushTable(builder, 'TSI5', + otfcc_buildTSI5(font->TSI5, options, font->glyf->length)); } if (options->dummy_DSIG) { diff --git a/lib/otf-writer/stat.c b/lib/otf-writer/stat.c index ae076e2b..d887f3d5 100644 --- a/lib/otf-writer/stat.c +++ b/lib/otf-writer/stat.c @@ -210,7 +210,7 @@ static void statHmtx(otfcc_Font *font, const otfcc_Options *options) { font->hhea->minLeftSideBearing = minLSB; font->hhea->minRightSideBearing = minRSB; font->hhea->xMaxExtent = maxExtent; - font->hhea->advanceWithMax = maxWidth; + font->hhea->advanceWidthMax = maxWidth; font->hmtx = hmtx; // set bit 1 in head.flags font->head->flags = (font->head->flags & (~0x2)) | (lsbAtX_0 ? 0x2 : 0); diff --git a/lib/support/bin-io.h b/lib/support/bin-io.h index ff9297b8..5d9f3c60 100644 --- a/lib/support/bin-io.h +++ b/lib/support/bin-io.h @@ -147,4 +147,12 @@ static INLINE int64_t read_64s(const uint8_t *src) { return (int64_t)read_64u(src); } +static INLINE uint16_t be16(uint16_t x) { + return ((x & 0xff) << 8) | ((x & 0xFF00) >> 8); +} +static INLINE uint32_t be32(uint32_t x) { + return ((x & 0x000000ff) << 24) | ((x & 0x0000FF00) << 8) | ((x & 0x00FF0000) >> 8) | + ((x & 0xFF000000) >> 24); +} + #endif diff --git a/lib/support/element-impl.h b/lib/support/element-impl.h index bac6728b..b65a403f 100644 --- a/lib/support/element-impl.h +++ b/lib/support/element-impl.h @@ -154,19 +154,13 @@ #define caryll_standardType(T, __name, ...) \ caryll_standardTypeFn(T, ##__VA_ARGS__); \ - caryll_ElementInterfaceOf(T) __name = { \ - caryll_standardTypeMethods(T), \ - }; + caryll_ElementInterfaceOf(T) __name = {caryll_standardTypeMethods(T)}; #define caryll_standardRefType(T, __name, ...) \ caryll_standardRefTypeFn(T, ##__VA_ARGS__); \ - caryll_ElementInterfaceOf(T) __name = { \ - caryll_standardRefTypeMethods(T), \ - }; + caryll_ElementInterfaceOf(T) __name = {caryll_standardRefTypeMethods(T)}; #define caryll_standardValType(T, __name, ...) \ caryll_standardValTypeFn(T, ##__VA_ARGS__); \ - caryll_ElementInterfaceOf(T) __name = { \ - caryll_standardValTypeMethods(T), \ - }; + caryll_ElementInterfaceOf(T) __name = {caryll_standardValTypeMethods(T)}; #else @@ -181,19 +175,13 @@ #define caryll_standardType(T, __name, ...) \ caryll_standardTypeFn(T, __VA_ARGS__); \ - caryll_ElementInterfaceOf(T) __name = { \ - caryll_standardTypeMethods(T), \ - }; + caryll_ElementInterfaceOf(T) __name = {caryll_standardTypeMethods(T)}; #define caryll_standardRefType(T, __name, ...) \ caryll_standardRefTypeFn(T, __VA_ARGS__); \ - caryll_ElementInterfaceOf(T) __name = { \ - caryll_standardRefTypeMethods(T), \ - }; + caryll_ElementInterfaceOf(T) __name = {caryll_standardRefTypeMethods(T)}; #define caryll_standardValType(T, __name, ...) \ caryll_standardValTypeFn(T, __VA_ARGS__); \ - caryll_ElementInterfaceOf(T) __name = { \ - caryll_standardValTypeMethods(T), \ - }; + caryll_ElementInterfaceOf(T) __name = {caryll_standardValTypeMethods(T)}; #endif diff --git a/lib/support/json/json-funcs.h b/lib/support/json/json-funcs.h index ad03cdee..55e2d98e 100644 --- a/lib/support/json/json-funcs.h +++ b/lib/support/json/json-funcs.h @@ -11,6 +11,7 @@ #include "caryll/ownership.h" #include "otfcc/primitives.h" #include "otfcc/vf/vq.h" +#include "otfcc/table/fvar.h" #ifndef INLINE #ifdef _MSC_VER @@ -20,6 +21,8 @@ #endif #endif +static INLINE json_value *preserialize(MOVE json_value *x); + static INLINE json_value *json_obj_get(const json_value *obj, const char *key) { if (!obj || obj->type != json_object) return NULL; for (uint32_t _k = 0; _k < obj->u.object.length; _k++) { @@ -49,7 +52,13 @@ static INLINE char *json_obj_getstr_share(const json_value *obj, const char *key return v->u.string.ptr; } -// Coordinates and VQ +static INLINE json_value *json_object_push_tag(json_value *a, uint32_t tag, json_value *b) { + char tags[4] = {(tag & 0xff000000) >> 24, (tag & 0xff0000) >> 16, (tag & 0xff00) >> 8, + (tag & 0xff)}; + return json_object_push_length(a, 4, tags, b); +} + +// Coordinates, VV and VQ static INLINE double json_numof(const json_value *cv) { if (cv && cv->type == json_integer) return cv->u.integer; if (cv && cv->type == json_double) return cv->u.dbl; @@ -62,12 +71,12 @@ static INLINE json_value *json_new_position(pos_t z) { return json_double_new(z); } } -static INLINE VQ json_vqOf(json_value *cv) { - return iVQ.createStill(json_numof(cv)); -} -static INLINE json_value *json_new_VQ(VQ z) { - return json_new_position(iVQ.getStill(z)); -} +json_value *json_new_VQRegion_Explicit(const vq_Region *rs, const table_fvar *fvar); +json_value *json_new_VQRegion(const vq_Region *rs, const table_fvar *fvar); +json_value *json_new_VQ(const VQ z, const table_fvar *fvar); +json_value *json_new_VV(const VV x, const table_fvar *fvar); +json_value *json_new_VVp(const VV *x, const table_fvar *fvar); +VQ json_vqOf(const json_value *cv, const table_fvar *fvar); static INLINE double json_obj_getnum(const json_value *obj, const char *key) { if (!obj || obj->type != json_object) return 0.0; diff --git a/lib/support/otfcc-alloc.h b/lib/support/otfcc-alloc.h index de9450e1..51b57442 100644 --- a/lib/support/otfcc-alloc.h +++ b/lib/support/otfcc-alloc.h @@ -54,16 +54,24 @@ static INLINE void *__caryll_reallocate(void *ptr, size_t n, unsigned long line) } } #ifdef __cplusplus -#define NEW_CLEAN_1(ptr) ptr = (decltype(ptr))__caryll_allocate_clean(sizeof(decltype(*ptr)), __LINE__) -#define NEW_CLEAN_N(ptr, n) ptr = (decltype(ptr))__caryll_allocate_clean(sizeof(decltype(*ptr)) * (n), __LINE__) -#define NEW_DIRTY(ptr) ptr = (decltype(ptr))__caryll_allocate_dirty(sizeof(decltype(*ptr)), __LINE__) +#define NEW_CLEAN_S(ptr, size) ptr = __caryll_allocate_clean((size), __LINE__) +#define NEW_CLEAN_1(ptr) \ + ptr = (decltype(ptr))__caryll_allocate_clean(sizeof(decltype(*ptr)), __LINE__) +#define NEW_CLEAN_N(ptr, n) \ + ptr = (decltype(ptr))__caryll_allocate_clean(sizeof(decltype(*ptr)) * (n), __LINE__) +#define NEW_DIRTY(ptr) \ + ptr = (decltype(ptr))__caryll_allocate_dirty(sizeof(decltype(*ptr)), __LINE__) +#define NEW_DIRTY_N(ptr, n) \ + ptr = (decltype(ptr))__caryll_allocate_dirty(sizeof(decltype(*ptr)) * (n), __LINE__) #define FREE(ptr) (__caryll_free(ptr), ptr = nullptr) #define DELETE(fn, ptr) (fn(ptr), ptr = nullptr) #define RESIZE(ptr, n) ptr = (decltype(ptr))__caryll_reallocate(ptr, sizeof(*ptr) * (n), __LINE__) #else +#define NEW_CLEAN_S(ptr, size) ptr = __caryll_allocate_clean((size), __LINE__) #define NEW_CLEAN_1(ptr) ptr = __caryll_allocate_clean(sizeof(*ptr), __LINE__) #define NEW_CLEAN_N(ptr, n) ptr = __caryll_allocate_clean(sizeof(*ptr) * (n), __LINE__) #define NEW_DIRTY(ptr) ptr = __caryll_allocate_dirty(sizeof(*ptr), __LINE__) +#define NEW_DIRTY_N(ptr, n) ptr = __caryll_allocate_dirty(sizeof(*ptr) * (n), __LINE__) #define FREE(ptr) (__caryll_free(ptr), ptr = NULL) #define DELETE(fn, ptr) (fn(ptr), ptr = NULL) #define RESIZE(ptr, n) ptr = __caryll_reallocate(ptr, sizeof(*ptr) * (n), __LINE__) diff --git a/lib/support/primitives.c b/lib/support/primitives.c index d8368a61..2f2ad360 100644 --- a/lib/support/primitives.c +++ b/lib/support/primitives.c @@ -1,5 +1,6 @@ -#include #include "otfcc/primitives.h" +#include "bin-io.h" +#include // f2dot14 type double otfcc_from_f2dot14(const f2dot14 x) { @@ -16,3 +17,50 @@ double otfcc_from_fixed(const f16dot16 x) { f16dot16 otfcc_to_fixed(const double x) { return round(x * 65536.0); } + +// F16.16 arith +// Clamp: remove too-large values +static INLINE f16dot16 clamp(int64_t value) { + int64_t tmp = value; + if (tmp < (int64_t)f16dot16_negativeIntinity) tmp = (int64_t)f16dot16_negativeIntinity; + if (tmp > (int64_t)f16dot16_infinity) tmp = (int64_t)f16dot16_infinity; + return (f16dot16)tmp; +} + +f16dot16 otfcc_f1616_add(f16dot16 a, f16dot16 b) { + return a + b; +} +f16dot16 otfcc_f1616_minus(f16dot16 a, f16dot16 b) { + return a - b; +} + +f16dot16 otfcc_f1616_multiply(f16dot16 a, f16dot16 b) { + int64_t tmp = (int64_t)a * (int64_t)b + f16dot16_k; + f16dot16 product = clamp(tmp >> f16dot16_precision); + return product; +} + +static INLINE f16dot16 divide(int64_t a, int32_t b) { + if (b == 0) { + if (a < 0) + return f16dot16_negativeIntinity; + else + return f16dot16_infinity; + } + + if ((a < 0 != b < 0)) { + a -= b / 2; + } else { + a += b / 2; + } + + return (f16dot16)(clamp(a / b)); +} + +f16dot16 otfcc_f1616_muldiv(f16dot16 a, f16dot16 b, f16dot16 c) { + int64_t tmp = (int64_t)a * (int64_t)b + f16dot16_k; + return divide(tmp, c); +} +f16dot16 otfcc_f1616_divide(f16dot16 a, f16dot16 b) { + return divide((int64_t)a << f16dot16_precision, b); +} diff --git a/lib/support/tag.h b/lib/support/tag.h index f723a644..7d141150 100644 --- a/lib/support/tag.h +++ b/lib/support/tag.h @@ -13,14 +13,11 @@ #endif // Tag handler -static INLINE char *tag2str(uint32_t tag) { - char *tags = (char *)malloc(sizeof(char) * 5); +static INLINE void tag2str(uint32_t tag, char tags[4]) { tags[0] = (tag >> 24) & 0xFF; tags[1] = (tag >> 16) & 0xFF; tags[2] = (tag >> 8) & 0xFF; tags[3] = tag & 0xFF; - tags[4] = 0; - return tags; } static INLINE uint32_t str2tag(char *tags) { diff --git a/lib/support/vector-impl.h b/lib/support/vector-impl.h index 13ea8374..f38153d6 100644 --- a/lib/support/vector-impl.h +++ b/lib/support/vector-impl.h @@ -60,7 +60,7 @@ if (target <= arr->capacity) return; \ if (arr->capacity < __CARYLL_VECTOR_INITIAL_SIZE) \ arr->capacity = __CARYLL_VECTOR_INITIAL_SIZE; \ - while (arr->capacity <= target) { \ + while (arr->capacity < target) { \ arr->capacity += arr->capacity / 2; \ } \ if (arr->items) { \ @@ -73,13 +73,24 @@ if (target <= arr->capacity) return; \ if (arr->capacity < __CARYLL_VECTOR_INITIAL_SIZE) \ arr->capacity = __CARYLL_VECTOR_INITIAL_SIZE; \ - if (arr->capacity <= target) { arr->capacity = target + 1; } \ + if (arr->capacity < target) { arr->capacity = target + 1; } \ if (arr->items) { \ arr->items = __caryll_realloc(arr->items, arr->capacity * sizeof(__T)); \ } else { \ arr->items = __caryll_calloc(arr->capacity, sizeof(__T)); \ } \ } \ + static __CARYLL_INLINE__ void __TV##_resizeTo(MODIFY __TV *arr, size_t target) { \ + arr->capacity = target; \ + if (arr->items) { \ + arr->items = __caryll_realloc(arr->items, arr->capacity * sizeof(__T)); \ + } else { \ + arr->items = __caryll_calloc(arr->capacity, sizeof(__T)); \ + } \ + } \ + static __CARYLL_INLINE__ void __TV##_shrinkToFit(MODIFY __TV *arr) { \ + __TV##_resizeTo(arr, arr->length); \ + } \ static __CARYLL_INLINE__ void __TV##_grow(MODIFY __TV *arr) { \ __TV##_growTo(arr, arr->length + 1); \ } \ @@ -168,7 +179,7 @@ .initCapN = __TV##_initCapN, .clear = __TV##_dispose, .replace = __TV##_replace, \ .copyReplace = __TV##_copyReplace, .push = __TV##_push, .pop = __TV##_pop, \ .fill = __TV##_fill, .sort = __TV##_sort, .disposeItem = __TV##_disposeItem, \ - .filterEnv = __TV##_filterEnv, .move = __TV##_move + .filterEnv = __TV##_filterEnv, .move = __TV##_move, .shrinkToFit = __TV##_shrinkToFit #define caryll_standardVectorImpl(__TV, __T, __ti, __name) \ caryll_VectorImplFunctions(__TV, __T, __ti); \ diff --git a/lib/table/BASE.c b/lib/table/BASE.c index 79bf2cb1..8edb3c75 100644 --- a/lib/table/BASE.c +++ b/lib/table/BASE.c @@ -132,18 +132,20 @@ static json_value *axisToJson(const otl_BaseAxis *axis) { if (!axis->entries[j].tag) continue; json_value *_entry = json_object_new(3); if (axis->entries[j].defaultBaselineTag) { - json_object_push( - _entry, "defaultBaseline", - json_string_new_nocopy(4, tag2str(axis->entries[j].defaultBaselineTag))); + char tag[4]; + tag2str(axis->entries[j].defaultBaselineTag, tag); + json_object_push(_entry, "defaultBaseline", json_string_new_length(4, tag)); } json_value *_values = json_object_new(axis->entries[j].baseValuesCount); for (tableid_t k = 0; k < axis->entries[j].baseValuesCount; k++) { - if (axis->entries[j].baseValues[k].tag) - json_object_push(_values, tag2str(axis->entries[j].baseValues[k].tag), - json_new_position(axis->entries[j].baseValues[k].coordinate)); + if (axis->entries[j].baseValues[k].tag) { + + json_object_push_tag(_values, axis->entries[j].baseValues[k].tag, + json_new_position(axis->entries[j].baseValues[k].coordinate)); + } } json_object_push(_entry, "baselines", _values); - json_object_push(_axis, tag2str(axis->entries[j].tag), _entry); + json_object_push_tag(_axis, axis->entries[j].tag, _entry); } return _axis; } @@ -295,10 +297,11 @@ bk_Block *axisToBk(const otl_BaseAxis *axis) { bkover), bkover); } else { - bk_push(baseValues, // assign a zero value - p16, bk_new_Block(b16, 1, // format - b16, 0, // coordinate - bkover), + bk_push(baseValues, // assign a zero value + p16, + bk_new_Block(b16, 1, // format + b16, 0, // coordinate + bkover), bkover); } } diff --git a/lib/table/CFF.c b/lib/table/CFF.c index d58e48fe..6e6ae7bd 100644 --- a/lib/table/CFF.c +++ b/lib/table/CFF.c @@ -326,7 +326,8 @@ static void callback_draw_sethint(void *_context, bool isVertical, double positi outline_builder_context *context = (outline_builder_context *)_context; glyf_iStemDefList.push((isVertical ? &context->g->stemV : &context->g->stemH), // ((glyf_PostscriptStemDef){ - .position = position, .width = width, + .position = position, + .width = width, })); } static void callback_draw_setmask(void *_context, bool isContourMask, bool *maskArray) { @@ -461,7 +462,9 @@ static void buildOutline(glyphid_t i, cff_extract_context *context, const otfcc_ // mainly due to curveto. We can remove that. This is unnecessary. glyf_iContour.pop(contour); } + glyf_iContour.shrinkToFit(contour); } + glyf_iContourList.shrinkToFit(&g->contours); iVQ.dispose(&cx), iVQ.dispose(&cy); cff_iIndex.dispose(&localSubrs); @@ -469,58 +472,100 @@ static void buildOutline(glyphid_t i, cff_extract_context *context, const otfcc_ context->seed = bc.randx; } +static sds formCIDString(cffsid_t cid) { + return sdscatprintf(sdsnew("CID"), "%d", cid); +} + static void nameGlyphsAccordingToCFF(cff_extract_context *context) { - if (context->meta->isCID) return; cff_File *cffFile = context->cffFile; table_glyf *glyphs = context->glyphs; - switch (cffFile->charsets.t) { - case cff_CHARSET_FORMAT0: { - for (glyphid_t j = 0; j < cffFile->charsets.s; j++) { - cffsid_t sid = cffFile->charsets.f0.glyph[j]; - sds glyphname = sdsget_cff_sid(sid, cffFile->string); - if (glyphname) { glyphs->items[j + 1]->name = glyphname; } + cff_Charset *charset = &cffFile->charsets; + if (context->meta->isCID) { + switch (charset->t) { + case cff_CHARSET_FORMAT0: { + for (glyphid_t j = 0; j < charset->s; j++) { + cffsid_t sid = charset->f0.glyph[j]; + sds glyphname = sdsget_cff_sid(sid, cffFile->string); + if (glyphname) { + glyphs->items[j + 1]->name = glyphname; + glyphs->items[j + 1]->cid = sid; + } + } + break; } - break; - } - case cff_CHARSET_FORMAT1: { - uint32_t glyphsNamedSofar = 1; - for (glyphid_t j = 0; j < cffFile->charsets.s; j++) { - glyphid_t first = cffFile->charsets.f1.range1[j].first; - sds glyphname = sdsget_cff_sid(first, cffFile->string); - if (glyphsNamedSofar < glyphs->length && glyphname) { - glyphs->items[glyphsNamedSofar]->name = glyphname; + case cff_CHARSET_FORMAT1: { + uint32_t glyphsNamedSofar = 1; + for (glyphid_t j = 0; j < charset->s; j++) { + cffsid_t first = charset->f1.range1[j].first; + for (glyphid_t k = 0; k <= charset->f1.range1[j].nleft; k++) { + cffsid_t sid = first + k; + sds glyphname = formCIDString(sid); + if (glyphsNamedSofar < glyphs->length && glyphname) { + glyphs->items[glyphsNamedSofar]->name = glyphname; + glyphs->items[glyphsNamedSofar]->cid = sid; + } + glyphsNamedSofar++; + } } - glyphsNamedSofar++; - for (glyphid_t k = 0; k < cffFile->charsets.f1.range1[j].nleft; k++) { - cffsid_t sid = first + k + 1; - sds glyphname = sdsget_cff_sid(sid, cffFile->string); - if (glyphsNamedSofar < glyphs->length && glyphname) { - glyphs->items[glyphsNamedSofar]->name = glyphname; + break; + } + case cff_CHARSET_FORMAT2: { + uint32_t glyphsNamedSofar = 1; + for (glyphid_t j = 0; j < charset->s; j++) { + cffsid_t first = charset->f2.range2[j].first; + for (glyphid_t k = 0; k <= charset->f2.range2[j].nleft; k++) { + cffsid_t sid = first + k; + sds glyphname = formCIDString(sid); + if (glyphsNamedSofar < glyphs->length && glyphname) { + glyphs->items[glyphsNamedSofar]->name = glyphname; + glyphs->items[glyphsNamedSofar]->cid = sid; + } + glyphsNamedSofar++; } - glyphsNamedSofar++; } + break; } - break; } - case cff_CHARSET_FORMAT2: { - uint32_t glyphsNamedSofar = 1; - for (glyphid_t j = 0; j < cffFile->charsets.s; j++) { - glyphid_t first = cffFile->charsets.f2.range2[j].first; - sds glyphname = sdsget_cff_sid(first, cffFile->string); - if (glyphsNamedSofar < glyphs->length && glyphname) { - glyphs->items[glyphsNamedSofar]->name = glyphname; - } - glyphsNamedSofar++; - for (glyphid_t k = 0; k < cffFile->charsets.f2.range2[j].nleft; k++) { - cffsid_t sid = first + k + 1; + } else { + switch (charset->t) { + case cff_CHARSET_FORMAT0: { + for (glyphid_t j = 0; j < charset->s; j++) { + cffsid_t sid = charset->f0.glyph[j]; sds glyphname = sdsget_cff_sid(sid, cffFile->string); - if (glyphsNamedSofar < glyphs->length && glyphname) { - glyphs->items[glyphsNamedSofar]->name = glyphname; + if (glyphname) { glyphs->items[j + 1]->name = glyphname; } + } + break; + } + case cff_CHARSET_FORMAT1: { + uint32_t glyphsNamedSofar = 1; + for (glyphid_t j = 0; j < charset->s; j++) { + glyphid_t first = charset->f1.range1[j].first; + for (glyphid_t k = 0; k <= charset->f1.range1[j].nleft; k++) { + cffsid_t sid = first + k; + sds glyphname = sdsget_cff_sid(sid, cffFile->string); + if (glyphsNamedSofar < glyphs->length && glyphname) { + glyphs->items[glyphsNamedSofar]->name = glyphname; + } + glyphsNamedSofar++; } - glyphsNamedSofar++; } + break; + } + case cff_CHARSET_FORMAT2: { + uint32_t glyphsNamedSofar = 1; + for (glyphid_t j = 0; j < charset->s; j++) { + glyphid_t first = charset->f2.range2[j].first; + for (glyphid_t k = 0; k <= charset->f2.range2[j].nleft; k++) { + cffsid_t sid = first + k; + sds glyphname = sdsget_cff_sid(sid, cffFile->string); + if (glyphsNamedSofar < glyphs->length && glyphname) { + glyphs->items[glyphsNamedSofar]->name = glyphname; + } + glyphsNamedSofar++; + } + } + break; } - break; } } } @@ -703,8 +748,8 @@ static json_value *fdToJson(const table_CFF *table) { json_object_push(_fontMatrix, "b", json_double_new(table->fontMatrix->b)); json_object_push(_fontMatrix, "c", json_double_new(table->fontMatrix->c)); json_object_push(_fontMatrix, "d", json_double_new(table->fontMatrix->d)); - json_object_push(_fontMatrix, "x", json_new_VQ(table->fontMatrix->x)); - json_object_push(_fontMatrix, "y", json_new_VQ(table->fontMatrix->y)); + json_object_push(_fontMatrix, "x", json_new_VQ(table->fontMatrix->x, NULL)); + json_object_push(_fontMatrix, "y", json_new_VQ(table->fontMatrix->y, NULL)); json_object_push(_CFF_, "fontMatrix", _fontMatrix); } if (table->privateDict) { json_object_push(_CFF_, "privates", pdToJson(table->privateDict)); } @@ -1261,7 +1306,7 @@ static caryll_Buffer *writecff_CIDKeyed(table_CFF *cff, table_glyf *glyf, (delta_size >> 24) & 0xff, (delta_size >> 16) & 0xff, (delta_size >> 8) & 0xff, delta_size & 0xff) // offset 2 - ); + ); bufwrite_bufdel(blob, t); // top dict body diff --git a/lib/table/VDMX.h b/lib/table/VDMX.h new file mode 100644 index 00000000..e74d39d3 --- /dev/null +++ b/lib/table/VDMX.h @@ -0,0 +1,11 @@ +#ifndef CARYLL_TABLE_VDMX_H +#define CARYLL_TABLE_VDMX_H + +#include "otfcc/table/VDMX.h" + +table_VDMX *otfcc_readVDMX(const otfcc_Packet packet, const otfcc_Options *options); +void otfcc_dumpVDMX(const table_VDMX *table, json_value *root, const otfcc_Options *options); +table_VDMX *otfcc_parseVDMX(const json_value *root, const otfcc_Options *options); +caryll_Buffer *otfcc_buildVDMX(const table_VDMX *vdmx, const otfcc_Options *options); + +#endif diff --git a/lib/table/all.h b/lib/table/all.h index 27f907c6..e3c44008 100644 --- a/lib/table/all.h +++ b/lib/table/all.h @@ -1,6 +1,8 @@ #ifndef CARYLL_TABLES_ALL_H #define CARYLL_TABLES_ALL_H +#include "table/fvar.h" + #include "table/head.h" #include "table/glyf.h" #include "table/CFF.h" @@ -18,6 +20,7 @@ #include "table/cvt.h" #include "table/fpgm-prep.h" #include "table/gasp.h" +#include "table/VDMX.h" #include "table/LTSH.h" #include "table/VORG.h" diff --git a/lib/table/fvar.c b/lib/table/fvar.c new file mode 100644 index 00000000..51d7ca33 --- /dev/null +++ b/lib/table/fvar.c @@ -0,0 +1,329 @@ +#include "fvar.h" + +#include "support/util.h" + +// fvar instance tuple +// fvar instance +static INLINE void initFvarInstance(fvar_Instance *inst) { + memset(inst, 0, sizeof(*inst)); + iVV.init(&inst->coordinates); +} +static INLINE void disposeFvarInstance(fvar_Instance *inst) { + iVV.dispose(&inst->coordinates); +} +caryll_standardType(fvar_Instance, fvar_iInstance, initFvarInstance, disposeFvarInstance); +caryll_standardVectorImpl(fvar_InstanceList, fvar_Instance, fvar_iInstance, fvar_iInstanceList); +// table fvar + +static INLINE void disposeFvarMaster(fvar_Master *m) { + sdsfree(m->name); + vq_deleteRegion(m->region); +} + +static INLINE void initFvar(table_fvar *fvar) { + memset(fvar, 0, sizeof(*fvar)); + vf_iAxes.init(&fvar->axes); + fvar_iInstanceList.init(&fvar->instances); +} +static INLINE void disposeFvar(table_fvar *fvar) { + vf_iAxes.dispose(&fvar->axes); + fvar_iInstanceList.dispose(&fvar->instances); + + fvar_Master *current, *tmp; + HASH_ITER(hh, fvar->masters, current, tmp) { + HASH_DEL(fvar->masters, current); + disposeFvarMaster(current); + FREE(current); + } +} + +static const vq_Region *fvar_registerRegion(table_fvar *fvar, MOVE vq_Region *region) { + fvar_Master *m = NULL; + HASH_FIND(hh, fvar->masters, region, VQ_REGION_SIZE(region->dimensions), m); + if (m) { + vq_deleteRegion(region); + return m->region; + } else { + NEW_CLEAN_1(m); + sds sMasterID = sdsfromlonglong(1 + HASH_CNT(hh, fvar->masters)); + m->name = sdscatsds(sdsnew("m"), sMasterID); + sdsfree(sMasterID); + m->region = region; + HASH_ADD_KEYPTR(hh, fvar->masters, m->region, VQ_REGION_SIZE(region->dimensions), m); + return m->region; + } +} + +static const fvar_Master *fvar_findMasterByRegion(const table_fvar *fvar, const vq_Region *region) { + fvar_Master *m = NULL; + HASH_FIND(hh, fvar->masters, region, VQ_REGION_SIZE(region->dimensions), m); + return m; +} + +caryll_standardRefTypeFn(table_fvar, initFvar, disposeFvar); +caryll_ElementInterfaceOf(table_fvar) table_iFvar = {caryll_standardRefTypeMethods(table_fvar), + .registerRegion = fvar_registerRegion, + .findMasterByRegion = fvar_findMasterByRegion}; + +// Local typedefs for parsing axis record +#pragma pack(1) +struct FVARHeader { + uint16_t majorVersion; + uint16_t minorVersion; + uint16_t axesArrayOffset; + uint16_t reserved1; + uint16_t axisCount; + uint16_t axisSize; + uint16_t instanceCount; + uint16_t instanceSize; +}; + +struct VariationAxisRecord { + uint32_t axisTag; + f16dot16 minValue; + f16dot16 defaultValue; + f16dot16 maxValue; + uint16_t flags; + uint16_t axisNameID; +}; + +struct InstanceRecord { + uint16_t subfamilyNameID; + uint16_t flags; + f16dot16 coordinates[]; +}; +#pragma pack() + +table_fvar *otfcc_readFvar(const otfcc_Packet packet, const otfcc_Options *options) { + table_fvar *fvar = NULL; + FOR_TABLE('fvar', table) { + font_file_pointer data = table.data; + if (table.length < sizeof(struct FVARHeader)) goto FAIL; + + struct FVARHeader *header = (struct FVARHeader *)data; + if (be16(header->majorVersion) != 1) goto FAIL; + if (be16(header->minorVersion) != 0) goto FAIL; + if (be16(header->axesArrayOffset) == 0) goto FAIL; + if (be16(header->axisCount) == 0) goto FAIL; + if (be16(header->axisSize) != sizeof(struct VariationAxisRecord)) goto FAIL; + uint16_t nAxes = be16(header->axisCount); + uint16_t instanceSizeWithoutPSNID = 4 + nAxes * sizeof(f16dot16); + uint16_t instanceSizeWithPSNID = 2 + instanceSizeWithoutPSNID; + if (be16(header->instanceSize) != instanceSizeWithoutPSNID && + be16(header->instanceSize) != instanceSizeWithPSNID) + goto FAIL; + if (table.length < be16(header->axesArrayOffset) + + sizeof(struct VariationAxisRecord) * nAxes + + be16(header->instanceSize) * be16(header->instanceCount)) + goto FAIL; + + fvar = table_iFvar.create(); + + // parse axes + struct VariationAxisRecord *axisRecord = + (struct VariationAxisRecord *)(data + be16(header->axesArrayOffset)); + for (uint16_t j = 0; j < nAxes; j++) { + vf_Axis axis = {.tag = be32(axisRecord->axisTag), + .minValue = otfcc_from_fixed(be32(axisRecord->minValue)), + .defaultValue = otfcc_from_fixed(be32(axisRecord->defaultValue)), + .maxValue = otfcc_from_fixed(be32(axisRecord->maxValue)), + .flags = be16(axisRecord->flags), + .axisNameID = be16(axisRecord->axisNameID)}; + vf_iAxes.push(&fvar->axes, axis); + axisRecord++; + } + + // parse instances + uint16_t nInstances = be16(header->instanceCount); + bool hasPostscriptNameID = be16(header->instanceSize) == instanceSizeWithPSNID; + struct InstanceRecord *instance = (struct InstanceRecord *)axisRecord; + for (uint16_t j = 0; j < nInstances; j++) { + fvar_Instance inst; + fvar_iInstance.init(&inst); + inst.subfamilyNameID = be16(instance->subfamilyNameID); + inst.flags = be16(instance->flags); + for (uint16_t k = 0; k < nAxes; k++) { + iVV.push(&inst.coordinates, otfcc_from_fixed(be32(instance->coordinates[k]))); + } + iVV.shrinkToFit(&inst.coordinates); + if (hasPostscriptNameID) { + inst.postScriptNameID = + be16(*(uint16_t *)(((font_file_pointer)instance) + instanceSizeWithoutPSNID)); + } + fvar_iInstanceList.push(&fvar->instances, inst); + instance = (struct InstanceRecord *)(((font_file_pointer)instance) + + be16(header->instanceSize)); + } + vf_iAxes.shrinkToFit(&fvar->axes); + fvar_iInstanceList.shrinkToFit(&fvar->instances); + + return fvar; + + FAIL: + logWarning("table 'fvar' corrupted.\n"); + table_iFvar.free(fvar); + fvar = NULL; + } + return NULL; +} + +void otfcc_dumpFvar(const table_fvar *table, json_value *root, const otfcc_Options *options) { + if (!table) return; + loggedStep("fvar") { + json_value *t = json_object_new(2); + // dump axes + json_value *_axes = json_object_new(table->axes.length); + foreach (vf_Axis *axis, table->axes) { + json_value *_axis = json_object_new(5); + json_object_push(_axis, "minValue", json_double_new(axis->minValue)); + json_object_push(_axis, "defaultValue", json_double_new(axis->defaultValue)); + json_object_push(_axis, "maxValue", json_double_new(axis->maxValue)); + json_object_push(_axis, "flags", json_integer_new(axis->flags)); + json_object_push(_axis, "axisNameID", json_integer_new(axis->axisNameID)); + json_object_push_tag(_axes, axis->tag, _axis); + } + json_object_push(t, "axes", _axes); + // dump instances + json_value *_instances = json_array_new(table->instances.length); + foreach (fvar_Instance *instance, table->instances) { + json_value *_instance = json_object_new(4); + json_object_push(_instance, "subfamilyNameID", + json_integer_new(instance->subfamilyNameID)); + if (instance->postScriptNameID) { + json_object_push(_instance, "postScriptNameID", + json_integer_new(instance->postScriptNameID)); + } + json_object_push(_instance, "flags", json_integer_new(instance->flags)); + json_object_push(_instance, "coordinates", json_new_VVp(&instance->coordinates, table)); + json_array_push(_instances, _instance); + } + json_object_push(t, "instances", _instances); + // dump masters + json_value *_masters = json_object_new(HASH_COUNT(table->masters)); + fvar_Master *current, *tmp; + HASH_ITER(hh, table->masters, current, tmp) { + json_object_push(_masters, current->name, + preserialize(json_new_VQRegion_Explicit(current->region, table))); + } + json_object_push(t, "masters", _masters); + json_object_push(root, "fvar", t); + } +} + +// JSON conversion functions +// dump + +json_value *json_new_VQSegment(const vq_Segment *s, const table_fvar *fvar) { + switch (s->type) { + case VQ_STILL:; + return json_new_position(s->val.still); + case VQ_DELTA:; + json_value *d = json_object_new(3); + json_object_push(d, "delta", json_new_position(s->val.delta.quantity)); + if (!s->val.delta.touched) { + json_object_push(d, "implicit", json_boolean_new(!s->val.delta.touched)); + } + json_object_push(d, "on", json_new_VQRegion(s->val.delta.region, fvar)); + return d; + default:; + return json_integer_new(0); + } +} +json_value *json_new_VQ(const VQ z, const table_fvar *fvar) { + + if (!z.shift.length) { + return preserialize(json_new_position(iVQ.getStill(z))); + } else { + json_value *a = json_array_new(z.shift.length + 1); + json_array_push(a, json_new_position(z.kernel)); + for (size_t j = 0; j < z.shift.length; j++) { + json_array_push(a, json_new_VQSegment(&z.shift.items[j], fvar)); + } + return preserialize(a); + } +} + +json_value *json_new_VV(const VV x, const table_fvar *fvar) { + const vf_Axes *axes = &fvar->axes; + if (axes && axes->length == x.length) { + json_value *_coord = json_object_new(axes->length); + for (size_t m = 0; m < x.length; m++) { + vf_Axis *axis = &axes->items[m]; + char tag[4] = {(axis->tag & 0xff000000) >> 24, (axis->tag & 0xff0000) >> 16, + (axis->tag & 0xff00) >> 8, (axis->tag & 0xff)}; + json_object_push_length(_coord, 4, tag, json_new_position(x.items[m])); + } + return preserialize(_coord); + } else { + json_value *_coord = json_array_new(x.length); + for (size_t m = 0; m < x.length; m++) { + json_array_push(_coord, json_new_position(x.items[m])); + } + return preserialize(_coord); + } +} +json_value *json_new_VVp(const VV *x, const table_fvar *fvar) { + const vf_Axes *axes = &fvar->axes; + + if (axes && axes->length == x->length) { + json_value *_coord = json_object_new(axes->length); + for (size_t m = 0; m < x->length; m++) { + vf_Axis *axis = &axes->items[m]; + char tag[4] = {(axis->tag & 0xff000000) >> 24, (axis->tag & 0xff0000) >> 16, + (axis->tag & 0xff00) >> 8, (axis->tag & 0xff)}; + json_object_push_length(_coord, 4, tag, json_new_position(x->items[m])); + } + return preserialize(_coord); + + } else { + json_value *_coord = json_array_new(x->length); + for (size_t m = 0; m < x->length; m++) { + json_array_push(_coord, json_new_position(x->items[m])); + } + return preserialize(_coord); + } +} +// parse +VQ json_vqOf(const json_value *cv, const table_fvar *fvar) { + return iVQ.createStill(json_numof(cv)); +} + +json_value *json_new_VQAxisSpan(const vq_AxisSpan *s) { + if (vq_AxisSpanIsOne(s)) { + return json_string_new("*"); + } else if ((s->peak > 0 && s->start == 0 && s->end == 1) || + (s->peak == 0 && s->start == -1 && s->end == 1) || + (s->peak < 0 && s->start == -1 && s->end == 0)) { + return json_new_position(s->peak); + } else { + json_value *a = json_object_new(3); + json_object_push(a, "start", json_new_position(s->start)); + json_object_push(a, "peak", json_new_position(s->peak)); + json_object_push(a, "end", json_new_position(s->end)); + return a; + } +} +json_value *json_new_VQRegion_Explicit(const vq_Region *rs, const table_fvar *fvar) { + const vf_Axes *axes = &fvar->axes; + if (axes && axes->length == rs->dimensions) { + json_value *r = json_object_new(rs->dimensions); + for (size_t j = 0; j < rs->dimensions; j++) { + json_object_push_tag(r, axes->items[j].tag, json_new_VQAxisSpan(&rs->spans[j])); + } + return r; + } else { + json_value *r = json_array_new(rs->dimensions); + for (size_t j = 0; j < rs->dimensions; j++) { + json_array_push(r, json_new_VQAxisSpan(&rs->spans[j])); + } + return r; + } +} +json_value *json_new_VQRegion(const vq_Region *rs, const table_fvar *fvar) { + const fvar_Master *m = table_iFvar.findMasterByRegion(fvar, rs); + if (m && m->name) { + return json_string_new_length((unsigned int)sdslen(m->name), m->name); + } else { + return json_new_VQRegion_Explicit(rs, fvar); + } +} diff --git a/lib/table/fvar.h b/lib/table/fvar.h new file mode 100644 index 00000000..33f29e96 --- /dev/null +++ b/lib/table/fvar.h @@ -0,0 +1,9 @@ +#ifndef CARYLL_TABLE_FVAR_H +#define CARYLL_TABLE_FVAR_H + +#include "otfcc/table/fvar.h" + +table_fvar *otfcc_readFvar(const otfcc_Packet packet, const otfcc_Options *options); +void otfcc_dumpFvar(const table_fvar *table, json_value *root, const otfcc_Options *options); + +#endif diff --git a/lib/table/glyf.c b/lib/table/glyf.c deleted file mode 100644 index 47ba503f..00000000 --- a/lib/table/glyf.c +++ /dev/null @@ -1,998 +0,0 @@ -#include "glyf.h" - -#include "support/util.h" -#include "support/ttinstr/ttinstr.h" - -// point -static void createPoint(glyf_Point *p) { - p->x = iVQ.createStill(0); - p->y = iVQ.createStill(0); - p->onCurve = true; -} -static void copyPoint(glyf_Point *dst, const glyf_Point *src) { - iVQ.copy(&dst->x, &src->x); - iVQ.copy(&dst->y, &src->y); - dst->onCurve = src->onCurve; -} -static void disposePoint(glyf_Point *p) { - iVQ.dispose(&p->x); - iVQ.dispose(&p->y); -} - -caryll_standardValType(glyf_Point, glyf_iPoint, createPoint, copyPoint, disposePoint); - -// contour -caryll_standardVectorImpl(glyf_Contour, glyf_Point, glyf_iPoint, glyf_iContour); -caryll_standardVectorImpl(glyf_ContourList, glyf_Contour, glyf_iContour, glyf_iContourList); - -// ref -static INLINE void initGlyfReference(glyf_ComponentReference *ref) { - ref->glyph = Handle.empty(); - ref->x = iVQ.createStill(0); - ref->y = iVQ.createStill(0); - ref->a = 1; - ref->b = 0; - ref->c = 0; - ref->d = 1; - ref->isAnchored = REF_XY; - ref->inner = ref->outer = 0; - ref->roundToGrid = false; - ref->useMyMetrics = false; -} -static void copyGlyfReference(glyf_ComponentReference *dst, const glyf_ComponentReference *src) { - iVQ.copy(&dst->x, &src->x); - iVQ.copy(&dst->y, &src->y); - Handle.copy(&dst->glyph, &src->glyph); - dst->a = src->a; - dst->b = src->b; - dst->c = src->c; - dst->d = src->d; - dst->isAnchored = src->isAnchored; - dst->inner = src->inner; - dst->outer = src->outer; - dst->roundToGrid = src->roundToGrid; - dst->useMyMetrics = src->useMyMetrics; -} -static INLINE void disposeGlyfReference(glyf_ComponentReference *ref) { - iVQ.dispose(&ref->x); - iVQ.dispose(&ref->y); - Handle.dispose(&ref->glyph); -} -caryll_standardValType(glyf_ComponentReference, glyf_iComponentReference, initGlyfReference, - copyGlyfReference, disposeGlyfReference); -caryll_standardVectorImpl(glyf_ReferenceList, glyf_ComponentReference, glyf_iComponentReference, - glyf_iReferenceList); - -// stem -caryll_standardType(glyf_PostscriptStemDef, glyf_iPostscriptStemDef); -caryll_standardVectorImpl(glyf_StemDefList, glyf_PostscriptStemDef, glyf_iPostscriptStemDef, - glyf_iStemDefList); - -// mask -caryll_standardType(glyf_PostscriptHintMask, glyf_iPostscriptHintMask); -caryll_standardVectorImpl(glyf_MaskList, glyf_PostscriptHintMask, glyf_iPostscriptHintMask, - glyf_iMaskList); - -typedef enum { - GLYF_FLAG_ON_CURVE = 1, - GLYF_FLAG_X_SHORT = (1 << 1), - GLYF_FLAG_Y_SHORT = (1 << 2), - GLYF_FLAG_REPEAT = (1 << 3), - GLYF_FLAG_SAME_X = (1 << 4), - GLYF_FLAG_SAME_Y = (1 << 5), - GLYF_FLAG_POSITIVE_X = (1 << 4), - GLYF_FLAG_POSITIVE_Y = (1 << 5) -} glyf_point_flag; - -typedef enum { - ARG_1_AND_2_ARE_WORDS = (1 << 0), - ARGS_ARE_XY_VALUES = (1 << 1), - ROUND_XY_TO_GRID = (1 << 2), - WE_HAVE_A_SCALE = (1 << 3), - MORE_COMPONENTS = (1 << 5), - WE_HAVE_AN_X_AND_Y_SCALE = (1 << 6), - WE_HAVE_A_TWO_BY_TWO = (1 << 7), - WE_HAVE_INSTRUCTIONS = (1 << 8), - USE_MY_METRICS = (1 << 9), - OVERLAP_COMPOUND = (1 << 10) -} glyf_reference_flag; - -typedef enum { MASK_ON_CURVE = 1 } glyf_oncurve_mask; - -glyf_Glyph *otfcc_newGlyf_glyph() { - glyf_Glyph *g; - NEW(g); - g->name = NULL; - g->horizontalOrigin = 0; - g->advanceWidth = 0; - g->verticalOrigin = 0; - g->advanceHeight = 0; - - glyf_iContourList.init(&g->contours); - glyf_iReferenceList.init(&g->references); - glyf_iStemDefList.init(&g->stemH); - glyf_iStemDefList.init(&g->stemV); - glyf_iMaskList.init(&g->hintMasks); - glyf_iMaskList.init(&g->contourMasks); - - g->instructionsLength = 0; - g->instructions = NULL; - g->fdSelect = Handle.empty(); - g->yPel = 0; - - g->stat.xMin = 0; - g->stat.xMax = 0; - g->stat.yMin = 0; - g->stat.yMax = 0; - g->stat.nestDepth = 0; - g->stat.nPoints = 0; - g->stat.nContours = 0; - g->stat.nCompositePoints = 0; - g->stat.nCompositeContours = 0; - return g; -} -static void otfcc_deleteGlyf_glyph(glyf_Glyph *g) { - if (!g) return; - sdsfree(g->name); - glyf_iContourList.dispose(&g->contours); - glyf_iReferenceList.dispose(&g->references); - glyf_iStemDefList.dispose(&g->stemH); - glyf_iStemDefList.dispose(&g->stemV); - glyf_iMaskList.dispose(&g->hintMasks); - glyf_iMaskList.dispose(&g->contourMasks); - if (g->instructions) { FREE(g->instructions); } - Handle.dispose(&g->fdSelect); - g->name = NULL; - FREE(g); -} - -static INLINE void initGlyfPtr(glyf_GlyphPtr *g) { - *g = NULL; -} -static void copyGlyfPtr(glyf_GlyphPtr *dst, const glyf_GlyphPtr *src) { - *dst = *src; -} -static INLINE void disposeGlyfPtr(glyf_GlyphPtr *g) { - otfcc_deleteGlyf_glyph(*g); -} -caryll_ElementInterfaceOf(glyf_GlyphPtr) glyf_iGlyphPtr = { - .init = initGlyfPtr, .copy = copyGlyfPtr, .dispose = disposeGlyfPtr, -}; -caryll_standardVectorImpl(table_glyf, glyf_GlyphPtr, glyf_iGlyphPtr, table_iGlyf); - -static glyf_Point *next_point(glyf_ContourList *contours, shapeid_t *cc, shapeid_t *cp) { - if (*cp >= contours->items[*cc].length) { - *cp = 0; - *cc += 1; - } - return &contours->items[*cc].items[(*cp)++]; -} - -static glyf_Glyph *otfcc_read_simple_glyph(font_file_pointer start, shapeid_t numberOfContours, - const otfcc_Options *options) { - glyf_Glyph *g = otfcc_newGlyf_glyph(); - glyf_ContourList *contours = &g->contours; - - shapeid_t pointsInGlyph = 0; - for (shapeid_t j = 0; j < numberOfContours; j++) { - shapeid_t lastPointInCurrentContour = read_16u(start + 2 * j); - glyf_Contour contour; - glyf_iContour.init(&contour); - glyf_iContour.fill(&contour, lastPointInCurrentContour - pointsInGlyph + 1); - glyf_iContourList.push(contours, contour); - pointsInGlyph = lastPointInCurrentContour + 1; - } - uint16_t instructionLength = read_16u(start + 2 * numberOfContours); - uint8_t *instructions = NULL; - if (instructionLength > 0) { - NEW(instructions, instructionLength); - memcpy(instructions, start + 2 * numberOfContours + 2, sizeof(uint8_t) * instructionLength); - } - g->instructionsLength = instructionLength; - g->instructions = instructions; - - // read flags - // There are repeating entries in the flags list, we will fill out the - // result - font_file_pointer flags; - NEW(flags, pointsInGlyph); - font_file_pointer flagStart = start + 2 * numberOfContours + 2 + instructionLength; - shapeid_t flagsReadSofar = 0; - shapeid_t flagBytesReadSofar = 0; - - shapeid_t currentContour = 0; - shapeid_t currentContourPointIndex = 0; - while (flagsReadSofar < pointsInGlyph) { - uint8_t flag = flagStart[flagBytesReadSofar]; - flags[flagsReadSofar] = flag; - flagBytesReadSofar += 1; - flagsReadSofar += 1; - next_point(contours, ¤tContour, ¤tContourPointIndex)->onCurve = - (flag & GLYF_FLAG_ON_CURVE); - if (flag & GLYF_FLAG_REPEAT) { // repeating flag - uint8_t repeat = flagStart[flagBytesReadSofar]; - flagBytesReadSofar += 1; - for (uint8_t j = 0; j < repeat; j++) { - flags[flagsReadSofar + j] = flag; - next_point(contours, ¤tContour, ¤tContourPointIndex)->onCurve = - (flag & GLYF_FLAG_ON_CURVE); - } - flagsReadSofar += repeat; - } - } - - // read X coordinates - font_file_pointer coordinatesStart = flagStart + flagBytesReadSofar; - uint32_t coordinatesOffset = 0; - shapeid_t coordinatesRead = 0; - currentContour = 0; - currentContourPointIndex = 0; - while (coordinatesRead < pointsInGlyph) { - uint8_t flag = flags[coordinatesRead]; - int16_t x; - if (flag & GLYF_FLAG_X_SHORT) { - x = (flag & GLYF_FLAG_POSITIVE_X ? 1 : -1) * - read_8u(coordinatesStart + coordinatesOffset); - coordinatesOffset += 1; - } else { - if (flag & GLYF_FLAG_SAME_X) { - x = 0; - } else { - x = read_16s(coordinatesStart + coordinatesOffset); - coordinatesOffset += 2; - } - } - iVQ.replace(&(next_point(contours, ¤tContour, ¤tContourPointIndex)->x), - iVQ.createStill(x)); - coordinatesRead += 1; - } - // read Y, identical to X - coordinatesRead = 0; - currentContour = 0; - currentContourPointIndex = 0; - while (coordinatesRead < pointsInGlyph) { - uint8_t flag = flags[coordinatesRead]; - int16_t y; - if (flag & GLYF_FLAG_Y_SHORT) { - y = (flag & GLYF_FLAG_POSITIVE_Y ? 1 : -1) * - read_8u(coordinatesStart + coordinatesOffset); - coordinatesOffset += 1; - } else { - if (flag & GLYF_FLAG_SAME_Y) { - y = 0; - } else { - y = read_16s(coordinatesStart + coordinatesOffset); - coordinatesOffset += 2; - } - } - iVQ.replace(&(next_point(contours, ¤tContour, ¤tContourPointIndex)->y), - iVQ.createStill(y)); - coordinatesRead += 1; - } - FREE(flags); - // turn deltas to absolute coordiantes - double cx = 0; - double cy = 0; - for (shapeid_t j = 0; j < numberOfContours; j++) { - for (shapeid_t k = 0; k < contours->items[j].length; k++) { - glyf_Point *z = &contours->items[j].items[k]; - cx += iVQ.getStill(z->x); - iVQ.dispose(&z->x); - z->x = iVQ.createStill(cx); - - cy += iVQ.getStill(z->y); - iVQ.dispose(&z->y); - z->y = iVQ.createStill(cy); - } - } - return g; -} - -static glyf_Glyph *otfcc_read_composite_glyph(font_file_pointer start, - const otfcc_Options *options) { - glyf_Glyph *g = otfcc_newGlyf_glyph(); - - // pass 1, read references quantity - uint16_t flags = 0; - uint32_t offset = 0; - bool glyphHasInstruction = false; - do { - flags = read_16u(start + offset); - glyphid_t index = read_16u(start + offset + 2); - - glyf_ComponentReference ref = glyf_iComponentReference.empty(); - ref.glyph = Handle.fromIndex(index); - - offset += 4; // flags & index - if (flags & ARGS_ARE_XY_VALUES) { - ref.isAnchored = REF_XY; - if (flags & ARG_1_AND_2_ARE_WORDS) { - ref.x = iVQ.createStill(read_16s(start + offset)); - ref.y = iVQ.createStill(read_16s(start + offset + 2)); - offset += 4; - } else { - ref.x = iVQ.createStill(read_8s(start + offset)); - ref.y = iVQ.createStill(read_8s(start + offset + 1)); - offset += 2; - } - } else { - ref.isAnchored = REF_ANCHOR_ANCHOR; - if (flags & ARG_1_AND_2_ARE_WORDS) { - ref.outer = read_16u(start + offset); - ref.inner = read_16u(start + offset + 2); - offset += 4; - } else { - ref.outer = read_8u(start + offset); - ref.inner = read_8u(start + offset + 1); - offset += 2; - } - } - if (flags & WE_HAVE_A_SCALE) { - ref.a = ref.d = otfcc_from_f2dot14(read_16s(start + offset)); - offset += 2; - } else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) { - ref.a = otfcc_from_f2dot14(read_16s(start + offset)); - ref.d = otfcc_from_f2dot14(read_16s(start + offset + 2)); - offset += 4; - } else if (flags & WE_HAVE_A_TWO_BY_TWO) { - ref.a = otfcc_from_f2dot14(read_16s(start + offset)); - ref.b = otfcc_from_f2dot14(read_16s(start + offset + 2)); - ref.c = otfcc_from_f2dot14(read_16s(start + offset + 4)); - ref.d = otfcc_from_f2dot14(read_16s(start + offset + 2)); - offset += 8; - } - ref.roundToGrid = flags & ROUND_XY_TO_GRID; - ref.useMyMetrics = flags & USE_MY_METRICS; - if (flags & WE_HAVE_INSTRUCTIONS) { glyphHasInstruction = true; } - glyf_iReferenceList.push(&g->references, ref); - } while (flags & MORE_COMPONENTS); - - if (glyphHasInstruction) { - uint16_t instructionLength = read_16u(start + offset); - font_file_pointer instructions = NULL; - if (instructionLength > 0) { - NEW(instructions, instructionLength); - memcpy(instructions, start + offset + 2, sizeof(uint8_t) * instructionLength); - } - g->instructionsLength = instructionLength; - g->instructions = instructions; - } else { - g->instructionsLength = 0; - g->instructions = NULL; - } - - return g; -} - -static glyf_Glyph *otfcc_read_glyph(font_file_pointer data, uint32_t offset, - const otfcc_Options *options) { - font_file_pointer start = data + offset; - int16_t numberOfContours = read_16u(start); - glyf_Glyph *g; - if (numberOfContours > 0) { - g = otfcc_read_simple_glyph(start + 10, numberOfContours, options); - } else { - g = otfcc_read_composite_glyph(start + 10, options); - } - g->stat.xMin = read_16s(start + 2); - g->stat.yMin = read_16s(start + 4); - g->stat.xMax = read_16s(start + 6); - g->stat.yMax = read_16s(start + 8); - return g; -} - -table_glyf *otfcc_readGlyf(const otfcc_Packet packet, const otfcc_Options *options, - table_head *head, table_maxp *maxp) { - if (head == NULL || maxp == NULL) return NULL; - uint32_t *offsets = NULL; - table_glyf *glyf = NULL; - - uint16_t locaIsLong = head->indexToLocFormat; - glyphid_t numGlyphs = maxp->numGlyphs; - NEW(offsets, (numGlyphs + 1)); - if (!offsets) goto ABSENT; - bool foundLoca = false; - - // read loca - FOR_TABLE('loca', table) { - font_file_pointer data = table.data; - uint32_t length = table.length; - if (length < 2 * numGlyphs + 2) goto LOCA_CORRUPTED; - for (uint32_t j = 0; j < numGlyphs + 1; j++) { - if (locaIsLong) { - offsets[j] = read_32u(data + j * 4); - } else { - offsets[j] = read_16u(data + j * 2) * 2; - } - if (j > 0 && offsets[j] < offsets[j - 1]) goto LOCA_CORRUPTED; - } - foundLoca = true; - break; - LOCA_CORRUPTED: - logWarning("table 'loca' corrupted.\n"); - if (offsets) { FREE(offsets), offsets = NULL; } - continue; - } - if (!foundLoca) goto ABSENT; - - // read glyf - FOR_TABLE('glyf', table) { - font_file_pointer data = table.data; - uint32_t length = table.length; - if (length < offsets[numGlyphs]) goto GLYF_CORRUPTED; - - glyf = table_iGlyf.create(); - - for (glyphid_t j = 0; j < numGlyphs; j++) { - if (offsets[j] < offsets[j + 1]) { // non-space glyph - table_iGlyf.push(glyf, otfcc_read_glyph(data, offsets[j], options)); - } else { // space glyph - table_iGlyf.push(glyf, otfcc_newGlyf_glyph()); - } - } - goto PRESENT; - GLYF_CORRUPTED: - logWarning("table 'glyf' corrupted.\n"); - if (glyf) { DELETE(table_iGlyf.free, glyf), glyf = NULL; } - } - goto ABSENT; - -PRESENT: - if (offsets) { FREE(offsets), offsets = NULL; } - return glyf; - -ABSENT: - if (offsets) { FREE(offsets), offsets = NULL; } - if (glyf) { FREE(glyf), glyf = NULL; } - return NULL; -} - -// to json -static void glyf_glyph_dump_contours(glyf_Glyph *g, json_value *target) { - if (!g->contours.length) return; - json_value *contours = json_array_new(g->contours.length); - for (shapeid_t k = 0; k < g->contours.length; k++) { - glyf_Contour *c = &(g->contours.items[k]); - json_value *contour = json_array_new(c->length); - for (shapeid_t m = 0; m < c->length; m++) { - json_value *point = json_object_new(4); - json_object_push(point, "x", json_new_VQ(c->items[m].x)); - json_object_push(point, "y", json_new_VQ(c->items[m].y)); - json_object_push(point, "on", json_boolean_new(c->items[m].onCurve & MASK_ON_CURVE)); - json_array_push(contour, point); - } - json_array_push(contours, contour); - } - json_object_push(target, "contours", preserialize(contours)); -} -static void glyf_glyph_dump_references(glyf_Glyph *g, json_value *target) { - if (!g->references.length) return; - json_value *references = json_array_new(g->references.length); - for (shapeid_t k = 0; k < g->references.length; k++) { - glyf_ComponentReference *r = &(g->references.items[k]); - json_value *ref = json_object_new(9); - json_object_push(ref, "glyph", - json_string_new_length((uint32_t)sdslen(r->glyph.name), r->glyph.name)); - json_object_push(ref, "x", json_new_VQ(r->x)); - json_object_push(ref, "y", json_new_VQ(r->y)); - json_object_push(ref, "a", json_new_position(r->a)); - json_object_push(ref, "b", json_new_position(r->b)); - json_object_push(ref, "c", json_new_position(r->c)); - json_object_push(ref, "d", json_new_position(r->d)); - if (r->isAnchored != REF_XY) { - json_object_push(ref, "isAnchored", json_boolean_new(true)); - json_object_push(ref, "inner", json_integer_new(r->inner)); - json_object_push(ref, "outer", json_integer_new(r->outer)); - } - if (r->roundToGrid) { json_object_push(ref, "roundToGrid", json_boolean_new(true)); } - if (r->useMyMetrics) { json_object_push(ref, "useMyMetrics", json_boolean_new(true)); } - json_array_push(references, ref); - } - json_object_push(target, "references", preserialize(references)); -} -static json_value *glyf_glyph_dump_stemdefs(glyf_StemDefList *stems) { - json_value *a = json_array_new(stems->length); - for (shapeid_t j = 0; j < stems->length; j++) { - json_value *stem = json_object_new(3); - json_object_push(stem, "position", json_new_position(stems->items[j].position)); - json_object_push(stem, "width", json_new_position(stems->items[j].width)); - json_array_push(a, stem); - } - return a; -} -static json_value *glyf_glyph_dump_maskdefs(glyf_MaskList *masks, glyf_StemDefList *hh, - glyf_StemDefList *vv) { - json_value *a = json_array_new(masks->length); - for (shapeid_t j = 0; j < masks->length; j++) { - json_value *mask = json_object_new(3); - json_object_push(mask, "contoursBefore", json_integer_new(masks->items[j].contoursBefore)); - json_object_push(mask, "pointsBefore", json_integer_new(masks->items[j].pointsBefore)); - json_value *h = json_array_new(hh->length); - for (shapeid_t k = 0; k < hh->length; k++) { - json_array_push(h, json_boolean_new(masks->items[j].maskH[k])); - } - json_object_push(mask, "maskH", h); - json_value *v = json_array_new(vv->length); - for (shapeid_t k = 0; k < vv->length; k++) { - json_array_push(v, json_boolean_new(masks->items[j].maskV[k])); - } - json_object_push(mask, "maskV", v); - json_array_push(a, mask); - } - return a; -} - -static json_value *glyf_dump_glyph(glyf_Glyph *g, const otfcc_Options *options, - bool hasVerticalMetrics, bool exportFDSelect) { - json_value *glyph = json_object_new(12); - json_object_push(glyph, "advanceWidth", json_new_position(g->advanceWidth)); - if (fabs(g->horizontalOrigin) > 1.0 / 1000.0) { - json_object_push(glyph, "horizontalOrigin", json_new_position(g->horizontalOrigin)); - } - if (hasVerticalMetrics) { - json_object_push(glyph, "advanceHeight", json_new_position(g->advanceHeight)); - json_object_push(glyph, "verticalOrigin", json_new_position(g->verticalOrigin)); - } - glyf_glyph_dump_contours(g, glyph); - glyf_glyph_dump_references(g, glyph); - if (exportFDSelect) json_object_push(glyph, "CFF_fdSelect", json_string_new(g->fdSelect.name)); - - // hinting data - if (!options->ignore_hints) { - if (g->instructions && g->instructionsLength) { - json_object_push(glyph, "instructions", - dump_ttinstr(g->instructions, g->instructionsLength, options)); - } - if (g->stemH.length) { - json_object_push(glyph, "stemH", preserialize(glyf_glyph_dump_stemdefs(&g->stemH))); - } - if (g->stemV.length) { - json_object_push(glyph, "stemV", preserialize(glyf_glyph_dump_stemdefs(&g->stemV))); - } - if (g->hintMasks.length) { - json_object_push(glyph, "hintMasks", preserialize(glyf_glyph_dump_maskdefs( - &g->hintMasks, &g->stemH, &g->stemV))); - } - if (g->contourMasks.length) { - json_object_push(glyph, "contourMasks", preserialize(glyf_glyph_dump_maskdefs( - &g->contourMasks, &g->stemH, &g->stemV))); - } - if (g->yPel) { json_object_push(glyph, "LTSH_yPel", json_integer_new(g->yPel)); } - } - return glyph; -} -void otfcc_dump_glyphorder(const table_glyf *table, json_value *root) { - if (!table) return; - json_value *order = json_array_new(table->length); - for (glyphid_t j = 0; j < table->length; j++) { - json_array_push(order, json_string_new_length((uint32_t)sdslen(table->items[j]->name), - table->items[j]->name)); - } - json_object_push(root, "glyph_order", preserialize(order)); -} -void otfcc_dumpGlyf(const table_glyf *table, json_value *root, const otfcc_Options *options, - bool hasVerticalMetrics, bool exportFDSelect) { - if (!table) return; - loggedStep("glyf") { - json_value *glyf = json_object_new(table->length); - for (glyphid_t j = 0; j < table->length; j++) { - glyf_Glyph *g = table->items[j]; - json_object_push(glyf, g->name, - glyf_dump_glyph(g, options, hasVerticalMetrics, exportFDSelect)); - } - json_object_push(root, "glyf", glyf); - if (!options->ignore_glyph_order) otfcc_dump_glyphorder(table, root); - } -} - -// from json -static glyf_Point glyf_parse_point(json_value *pointdump) { - glyf_Point point; - glyf_iPoint.init(&point); - if (!pointdump || pointdump->type != json_object) return point; - for (uint32_t _k = 0; _k < pointdump->u.object.length; _k++) { - char *ck = pointdump->u.object.values[_k].name; - json_value *cv = pointdump->u.object.values[_k].value; - if (strcmp(ck, "x") == 0) { - iVQ.replace(&point.x, json_vqOf(cv)); - } else if (strcmp(ck, "y") == 0) { - iVQ.replace(&point.y, json_vqOf(cv)); - } else if (strcmp(ck, "on") == 0) { - point.onCurve = json_boolof(cv); - } - } - return point; -} - -static void glyf_parse_contours(json_value *col, glyf_Glyph *g) { - if (!col) { return; } - shapeid_t nContours = col->u.array.length; - for (shapeid_t j = 0; j < nContours; j++) { - json_value *contourdump = col->u.array.values[j]; - glyf_Contour contour; - glyf_iContour.initCapN( - &contour, - contourdump && contourdump->type == json_array ? contourdump->u.array.length : 1); - if (contourdump && contourdump->type == json_array) { - for (shapeid_t k = 0; k < contourdump->u.array.length; k++) { - glyf_iContour.push(&contour, glyf_parse_point(contourdump->u.array.values[k])); - } - } - glyf_iContourList.push(&g->contours, contour); - } -} - -static glyf_ComponentReference glyf_parse_reference(json_value *refdump) { - json_value *_gname = json_obj_get_type(refdump, "glyph", json_string); - glyf_ComponentReference ref = glyf_iComponentReference.empty(); - if (_gname) { - ref.glyph = Handle.fromName(sdsnewlen(_gname->u.string.ptr, _gname->u.string.length)); - iVQ.replace(&ref.x, json_vqOf(json_obj_get(refdump, "x"))); - iVQ.replace(&ref.y, json_vqOf(json_obj_get(refdump, "y"))); - ref.a = json_obj_getnum_fallback(refdump, "a", 1.0); - ref.b = json_obj_getnum_fallback(refdump, "b", 0.0); - ref.c = json_obj_getnum_fallback(refdump, "c", 0.0); - ref.d = json_obj_getnum_fallback(refdump, "d", 1.0); - ref.roundToGrid = json_obj_getbool(refdump, "roundToGrid"); - ref.useMyMetrics = json_obj_getbool(refdump, "useMyMetrics"); - if (json_obj_getbool(refdump, "isAnchored")) { - ref.isAnchored = REF_ANCHOR_XY; - ref.inner = json_obj_getint(refdump, "inner"); - ref.outer = json_obj_getint(refdump, "outer"); - } - } else { - // Invalid glyph references - ref.glyph.name = NULL; - iVQ.replace(&ref.x, iVQ.createStill(0)); - iVQ.replace(&ref.y, iVQ.createStill(0)); - ref.a = 1.0; - ref.b = 0.0; - ref.c = 0.0; - ref.d = 1.0; - ref.roundToGrid = false; - ref.useMyMetrics = false; - } - return ref; -} -static void glyf_parse_references(json_value *col, glyf_Glyph *g) { - if (!col) { return; } - for (shapeid_t j = 0; j < col->u.array.length; j++) { - glyf_iReferenceList.push(&g->references, glyf_parse_reference(col->u.array.values[j])); - } -} - -static void makeInstrsForGlyph(void *_g, uint8_t *instrs, uint32_t len) { - glyf_Glyph *g = (glyf_Glyph *)_g; - g->instructionsLength = len; - g->instructions = instrs; -} -static void wrongInstrsForGlyph(void *_g, char *reason, int pos) { - glyf_Glyph *g = (glyf_Glyph *)_g; - fprintf(stderr, "[OTFCC] TrueType instructions parse error : %s, at %d in /%s\n", reason, pos, - g->name); -} - -static void parse_stems(json_value *sd, glyf_StemDefList *stems) { - if (!sd) return; - for (shapeid_t j = 0; j < sd->u.array.length; j++) { - json_value *s = sd->u.array.values[j]; - if (s->type != json_object) continue; - glyf_PostscriptStemDef sdef; - sdef.map = 0; - sdef.position = json_obj_getnum(s, "position"); - sdef.width = json_obj_getnum(s, "width"); - glyf_iStemDefList.push(stems, sdef); - } -} -static void parse_maskbits(bool *arr, json_value *bits) { - if (!bits) { - for (shapeid_t j = 0; j < 0x100; j++) { - arr[j] = false; - } - } else { - for (shapeid_t j = 0; j < 0x100 && j < bits->u.array.length; j++) { - json_value *b = bits->u.array.values[j]; - switch (b->type) { - case json_boolean: - arr[j] = b->u.boolean; - break; - case json_integer: - arr[j] = b->u.integer; - break; - case json_double: - arr[j] = b->u.dbl; - break; - default: - arr[j] = false; - } - } - } -} -static void parse_masks(json_value *md, glyf_MaskList *masks) { - if (!md) return; - for (shapeid_t j = 0; j < md->u.array.length; j++) { - json_value *m = md->u.array.values[j]; - if (m->type != json_object) continue; - - glyf_PostscriptHintMask mask; - mask.pointsBefore = json_obj_getint(m, "pointsBefore"); - mask.contoursBefore = json_obj_getint(m, "contoursBefore"); - parse_maskbits(&(mask.maskH[0]), json_obj_get_type(m, "maskH", json_array)); - parse_maskbits(&(mask.maskV[0]), json_obj_get_type(m, "maskV", json_array)); - glyf_iMaskList.push(masks, mask); - } -} - -static glyf_Glyph *otfcc_glyf_parse_glyph(json_value *glyphdump, otfcc_GlyphOrderEntry *order_entry, - const otfcc_Options *options) { - glyf_Glyph *g = otfcc_newGlyf_glyph(); - g->name = sdsdup(order_entry->name); - g->advanceWidth = json_obj_getnum(glyphdump, "advanceWidth"); - g->horizontalOrigin = json_obj_getnum(glyphdump, "horizontalOrigin"); - g->advanceHeight = json_obj_getnum(glyphdump, "advanceHeight"); - g->verticalOrigin = json_obj_getnum(glyphdump, "verticalOrigin"); - glyf_parse_contours(json_obj_get_type(glyphdump, "contours", json_array), g); - glyf_parse_references(json_obj_get_type(glyphdump, "references", json_array), g); - if (!options->ignore_hints) { - parse_ttinstr(json_obj_get(glyphdump, "instructions"), g, makeInstrsForGlyph, - wrongInstrsForGlyph); - parse_stems(json_obj_get_type(glyphdump, "stemH", json_array), &g->stemH); - parse_stems(json_obj_get_type(glyphdump, "stemV", json_array), &g->stemV); - parse_masks(json_obj_get_type(glyphdump, "hintMasks", json_array), &g->hintMasks); - parse_masks(json_obj_get_type(glyphdump, "contourMasks", json_array), &g->contourMasks); - g->yPel = json_obj_getint(glyphdump, "LTSH_yPel"); - } - // Glyph data of other tables - g->fdSelect = Handle.fromName(json_obj_getsds(glyphdump, "CFF_fdSelect")); - if (!g->yPel) { g->yPel = json_obj_getint(glyphdump, "yPel"); } - return g; -} - -table_glyf *otfcc_parseGlyf(const json_value *root, otfcc_GlyphOrder *glyph_order, - const otfcc_Options *options) { - if (root->type != json_object || !glyph_order) return NULL; - table_glyf *glyf = NULL; - json_value *table; - if ((table = json_obj_get_type(root, "glyf", json_object))) { - loggedStep("glyf") { - glyphid_t numGlyphs = table->u.object.length; - glyf = table_iGlyf.createN(numGlyphs); - for (glyphid_t j = 0; j < numGlyphs; j++) { - sds gname = sdsnewlen(table->u.object.values[j].name, - table->u.object.values[j].name_length); - json_value *glyphdump = table->u.object.values[j].value; - otfcc_GlyphOrderEntry *order_entry = NULL; - HASH_FIND(hhName, glyph_order->byName, gname, sdslen(gname), order_entry); - if (glyphdump->type == json_object && order_entry && - !glyf->items[order_entry->gid]) { - glyf->items[order_entry->gid] = - otfcc_glyf_parse_glyph(glyphdump, order_entry, options); - } - json_value_free(glyphdump); - json_value *v = json_null_new(); - v->parent = table; - table->u.object.values[j].value = v; - sdsfree(gname); - } - } - return glyf; - } - return NULL; -} - -caryll_Buffer *shrinkFlags(caryll_Buffer *flags) { - if (!buflen(flags)) return (flags); - caryll_Buffer *shrunk = bufnew(); - bufwrite8(shrunk, flags->data[0]); - int repeating = 0; - for (size_t j = 1; j < buflen(flags); j++) { - if (flags->data[j] == flags->data[j - 1]) { - if (repeating && repeating < 0xFE) { - shrunk->data[shrunk->cursor - 1] += 1; - repeating += 1; - } else if (repeating == 0) { - shrunk->data[shrunk->cursor - 1] |= GLYF_FLAG_REPEAT; - bufwrite8(shrunk, 1); - repeating += 1; - } else { - repeating = 0; - bufwrite8(shrunk, flags->data[j]); - } - } else { - repeating = 0; - bufwrite8(shrunk, flags->data[j]); - } - } - buffree(flags); - return shrunk; -} - -// serialize -#define EPSILON (1e-5) -static void glyf_build_simple(const glyf_Glyph *g, caryll_Buffer *gbuf) { - caryll_Buffer *flags = bufnew(); - caryll_Buffer *xs = bufnew(); - caryll_Buffer *ys = bufnew(); - - bufwrite16b(gbuf, g->contours.length); - bufwrite16b(gbuf, (int16_t)g->stat.xMin); - bufwrite16b(gbuf, (int16_t)g->stat.yMin); - bufwrite16b(gbuf, (int16_t)g->stat.xMax); - bufwrite16b(gbuf, (int16_t)g->stat.yMax); - - // endPtsOfContours[n] - shapeid_t ptid = 0; - for (shapeid_t j = 0; j < g->contours.length; j++) { - ptid += g->contours.items[j].length; - bufwrite16b(gbuf, ptid - 1); - } - - // instructions - bufwrite16b(gbuf, g->instructionsLength); - if (g->instructions) bufwrite_bytes(gbuf, g->instructionsLength, g->instructions); - - // flags and points - bufclear(flags); - bufclear(xs); - bufclear(ys); - int32_t cx = 0; - int32_t cy = 0; - for (shapeid_t cj = 0; cj < g->contours.length; cj++) { - for (shapeid_t k = 0; k < g->contours.items[cj].length; k++) { - glyf_Point *p = &(g->contours.items[cj].items[k]); - uint8_t flag = (p->onCurve & MASK_ON_CURVE) ? GLYF_FLAG_ON_CURVE : 0; - int32_t px = round(iVQ.getStill(p->x)); - int32_t py = round(iVQ.getStill(p->y)); - int16_t dx = (int16_t)(px - cx); - int16_t dy = (int16_t)(py - cy); - if (dx == 0) { - flag |= GLYF_FLAG_SAME_X; - } else if (dx >= -0xFF && dx <= 0xFF) { - flag |= GLYF_FLAG_X_SHORT; - if (dx > 0) { - flag |= GLYF_FLAG_POSITIVE_X; - bufwrite8(xs, dx); - } else { - bufwrite8(xs, -dx); - } - } else { - bufwrite16b(xs, dx); - } - - if (dy == 0) { - flag |= GLYF_FLAG_SAME_Y; - } else if (dy >= -0xFF && dy <= 0xFF) { - flag |= GLYF_FLAG_Y_SHORT; - if (dy > 0) { - flag |= GLYF_FLAG_POSITIVE_Y; - bufwrite8(ys, dy); - } else { - bufwrite8(ys, -dy); - } - } else { - bufwrite16b(ys, dy); - } - bufwrite8(flags, flag); - cx = px; - cy = py; - } - } - flags = shrinkFlags(flags); - bufwrite_buf(gbuf, flags); - bufwrite_buf(gbuf, xs); - bufwrite_buf(gbuf, ys); - - buffree(flags); - buffree(xs); - buffree(ys); -} -static void glyf_build_composite(const glyf_Glyph *g, caryll_Buffer *gbuf) { - bufwrite16b(gbuf, (-1)); - bufwrite16b(gbuf, (int16_t)g->stat.xMin); - bufwrite16b(gbuf, (int16_t)g->stat.yMin); - bufwrite16b(gbuf, (int16_t)g->stat.xMax); - bufwrite16b(gbuf, (int16_t)g->stat.yMax); - for (shapeid_t rj = 0; rj < g->references.length; rj++) { - glyf_ComponentReference *r = &(g->references.items[rj]); - uint16_t flags = - (rj < g->references.length - 1 ? MORE_COMPONENTS - : g->instructionsLength > 0 ? WE_HAVE_INSTRUCTIONS : 0); - bool outputAnchor = r->isAnchored == REF_ANCHOR_CONSOLIDATED; - - union { - uint16_t pointid; - int16_t coord; - } arg1, arg2; - - // flags - if (outputAnchor) { - arg1.pointid = r->outer; - arg2.pointid = r->inner; - if (!(arg1.pointid < 0x100 && arg2.pointid < 0x100)) { flags |= ARG_1_AND_2_ARE_WORDS; } - } else { - flags |= ARGS_ARE_XY_VALUES; - arg1.coord = iVQ.getStill(r->x); - arg2.coord = iVQ.getStill(r->y); - if (!(arg1.coord < 128 && arg1.coord >= -128 && arg2.coord < 128 && - arg2.coord >= -128)) { - flags |= ARG_1_AND_2_ARE_WORDS; - } - } - if (fabs(r->b) > EPSILON || fabs(r->c) > EPSILON) { - flags |= WE_HAVE_A_TWO_BY_TWO; - } else if (fabs(r->a - 1) > EPSILON || fabs(r->d - 1) > EPSILON) { - if (fabs(r->a - r->d) > EPSILON) { - flags |= WE_HAVE_AN_X_AND_Y_SCALE; - } else { - flags |= WE_HAVE_A_SCALE; - } - } - if (r->roundToGrid) flags |= ROUND_XY_TO_GRID; - if (r->useMyMetrics) flags |= USE_MY_METRICS; - bufwrite16b(gbuf, flags); - bufwrite16b(gbuf, r->glyph.index); - if (flags & ARG_1_AND_2_ARE_WORDS) { - bufwrite16b(gbuf, arg1.pointid); - bufwrite16b(gbuf, arg2.pointid); - } else { - bufwrite8(gbuf, arg1.pointid); - bufwrite8(gbuf, arg2.pointid); - } - if (flags & WE_HAVE_A_SCALE) { - bufwrite16b(gbuf, otfcc_to_f2dot14(r->a)); - } else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) { - bufwrite16b(gbuf, otfcc_to_f2dot14(r->a)); - bufwrite16b(gbuf, otfcc_to_f2dot14(r->d)); - } else if (flags & WE_HAVE_A_TWO_BY_TWO) { - bufwrite16b(gbuf, otfcc_to_f2dot14(r->a)); - bufwrite16b(gbuf, otfcc_to_f2dot14(r->b)); - bufwrite16b(gbuf, otfcc_to_f2dot14(r->c)); - bufwrite16b(gbuf, otfcc_to_f2dot14(r->d)); - } - } - if (g->instructionsLength) { - bufwrite16b(gbuf, g->instructionsLength); - if (g->instructions) bufwrite_bytes(gbuf, g->instructionsLength, g->instructions); - } -} -table_GlyfAndLocaBuffers otfcc_buildGlyf(const table_glyf *table, table_head *head, - const otfcc_Options *options) { - caryll_Buffer *bufglyf = bufnew(); - caryll_Buffer *bufloca = bufnew(); - if (table && head) { - caryll_Buffer *gbuf = bufnew(); - uint32_t *loca; - NEW(loca, table->length + 1); - for (glyphid_t j = 0; j < table->length; j++) { - loca[j] = (uint32_t)bufglyf->cursor; - glyf_Glyph *g = table->items[j]; - bufclear(gbuf); - if (g->contours.length > 0) { - glyf_build_simple(g, gbuf); - } else if (g->references.length > 0) { - glyf_build_composite(g, gbuf); - } - // pad extra zeroes - buflongalign(gbuf); - bufwrite_buf(bufglyf, gbuf); - } - loca[table->length] = (uint32_t)bufglyf->cursor; - if (bufglyf->cursor >= 0x20000) { - head->indexToLocFormat = 1; - } else { - head->indexToLocFormat = 0; - } - // write loca table - for (uint32_t j = 0; j <= table->length; j++) { - if (head->indexToLocFormat) { - bufwrite32b(bufloca, loca[j]); - } else { - bufwrite16b(bufloca, loca[j] >> 1); - } - } - buffree(gbuf); - FREE(loca); - } - table_GlyfAndLocaBuffers pair = {bufglyf, bufloca}; - return pair; -} diff --git a/lib/table/glyf.h b/lib/table/glyf.h index 79933046..ee7d0c6d 100644 --- a/lib/table/glyf.h +++ b/lib/table/glyf.h @@ -6,16 +6,54 @@ glyf_Glyph *otfcc_newGlyf_glyph(); void otfcc_initGlyfContour(glyf_Contour *contour); -table_glyf *otfcc_readGlyf(const otfcc_Packet packet, const otfcc_Options *options, table_head *head, table_maxp *maxp); -void otfcc_dumpGlyf(const table_glyf *table, json_value *root, const otfcc_Options *options, bool hasVerticalMetrics, - bool exportFDSelect); -table_glyf *otfcc_parseGlyf(const json_value *root, otfcc_GlyphOrder *glyph_order, const otfcc_Options *options); +typedef struct { + bool locaIsLong; + glyphid_t numGlyphs; + shapeid_t nPhantomPoints; + table_fvar *fvar; + bool hasVerticalMetrics; + bool exportFDSelect; +} GlyfIOContext; + +table_glyf *otfcc_readGlyf(const otfcc_Packet packet, const otfcc_Options *options, + const GlyfIOContext *ctx); +void otfcc_dumpGlyf(const table_glyf *table, json_value *root, const otfcc_Options *options, + const GlyfIOContext *ctx); +table_glyf *otfcc_parseGlyf(const json_value *root, otfcc_GlyphOrder *glyph_order, + const otfcc_Options *options); typedef struct { caryll_Buffer *glyf; caryll_Buffer *loca; } table_GlyfAndLocaBuffers; -table_GlyfAndLocaBuffers otfcc_buildGlyf(const table_glyf *table, table_head *head, const otfcc_Options *options); +table_GlyfAndLocaBuffers otfcc_buildGlyf(const table_glyf *table, table_head *head, + const otfcc_Options *options); + +typedef enum { + GLYF_FLAG_ON_CURVE = 1, + GLYF_FLAG_X_SHORT = (1 << 1), + GLYF_FLAG_Y_SHORT = (1 << 2), + GLYF_FLAG_REPEAT = (1 << 3), + GLYF_FLAG_SAME_X = (1 << 4), + GLYF_FLAG_SAME_Y = (1 << 5), + GLYF_FLAG_POSITIVE_X = (1 << 4), + GLYF_FLAG_POSITIVE_Y = (1 << 5) +} glyf_point_flag; + +typedef enum { + ARG_1_AND_2_ARE_WORDS = (1 << 0), + ARGS_ARE_XY_VALUES = (1 << 1), + ROUND_XY_TO_GRID = (1 << 2), + WE_HAVE_A_SCALE = (1 << 3), + MORE_COMPONENTS = (1 << 5), + WE_HAVE_AN_X_AND_Y_SCALE = (1 << 6), + WE_HAVE_A_TWO_BY_TWO = (1 << 7), + WE_HAVE_INSTRUCTIONS = (1 << 8), + USE_MY_METRICS = (1 << 9), + OVERLAP_COMPOUND = (1 << 10) +} glyf_reference_flag; + +typedef enum { MASK_ON_CURVE = 1 } glyf_oncurve_mask; #endif diff --git a/lib/table/glyf/build.c b/lib/table/glyf/build.c new file mode 100644 index 00000000..71fc70bb --- /dev/null +++ b/lib/table/glyf/build.c @@ -0,0 +1,221 @@ +#include "../glyf.h" + +#include "support/util.h" +#include "support/ttinstr/ttinstr.h" + +caryll_Buffer *shrinkFlags(caryll_Buffer *flags) { + if (!buflen(flags)) return (flags); + caryll_Buffer *shrunk = bufnew(); + bufwrite8(shrunk, flags->data[0]); + int repeating = 0; + for (size_t j = 1; j < buflen(flags); j++) { + if (flags->data[j] == flags->data[j - 1]) { + if (repeating && repeating < 0xFE) { + shrunk->data[shrunk->cursor - 1] += 1; + repeating += 1; + } else if (repeating == 0) { + shrunk->data[shrunk->cursor - 1] |= GLYF_FLAG_REPEAT; + bufwrite8(shrunk, 1); + repeating += 1; + } else { + repeating = 0; + bufwrite8(shrunk, flags->data[j]); + } + } else { + repeating = 0; + bufwrite8(shrunk, flags->data[j]); + } + } + buffree(flags); + return shrunk; +} + +// serialize +#define EPSILON (1e-5) +static void glyf_build_simple(const glyf_Glyph *g, caryll_Buffer *gbuf) { + caryll_Buffer *flags = bufnew(); + caryll_Buffer *xs = bufnew(); + caryll_Buffer *ys = bufnew(); + + bufwrite16b(gbuf, g->contours.length); + bufwrite16b(gbuf, (int16_t)g->stat.xMin); + bufwrite16b(gbuf, (int16_t)g->stat.yMin); + bufwrite16b(gbuf, (int16_t)g->stat.xMax); + bufwrite16b(gbuf, (int16_t)g->stat.yMax); + + // endPtsOfContours[n] + shapeid_t ptid = 0; + for (shapeid_t j = 0; j < g->contours.length; j++) { + ptid += g->contours.items[j].length; + bufwrite16b(gbuf, ptid - 1); + } + + // instructions + bufwrite16b(gbuf, g->instructionsLength); + if (g->instructions) bufwrite_bytes(gbuf, g->instructionsLength, g->instructions); + + // flags and points + bufclear(flags); + bufclear(xs); + bufclear(ys); + int32_t cx = 0; + int32_t cy = 0; + for (shapeid_t cj = 0; cj < g->contours.length; cj++) { + for (shapeid_t k = 0; k < g->contours.items[cj].length; k++) { + glyf_Point *p = &(g->contours.items[cj].items[k]); + uint8_t flag = (p->onCurve & MASK_ON_CURVE) ? GLYF_FLAG_ON_CURVE : 0; + int32_t px = round(iVQ.getStill(p->x)); + int32_t py = round(iVQ.getStill(p->y)); + int16_t dx = (int16_t)(px - cx); + int16_t dy = (int16_t)(py - cy); + if (dx == 0) { + flag |= GLYF_FLAG_SAME_X; + } else if (dx >= -0xFF && dx <= 0xFF) { + flag |= GLYF_FLAG_X_SHORT; + if (dx > 0) { + flag |= GLYF_FLAG_POSITIVE_X; + bufwrite8(xs, dx); + } else { + bufwrite8(xs, -dx); + } + } else { + bufwrite16b(xs, dx); + } + + if (dy == 0) { + flag |= GLYF_FLAG_SAME_Y; + } else if (dy >= -0xFF && dy <= 0xFF) { + flag |= GLYF_FLAG_Y_SHORT; + if (dy > 0) { + flag |= GLYF_FLAG_POSITIVE_Y; + bufwrite8(ys, dy); + } else { + bufwrite8(ys, -dy); + } + } else { + bufwrite16b(ys, dy); + } + bufwrite8(flags, flag); + cx = px; + cy = py; + } + } + flags = shrinkFlags(flags); + bufwrite_buf(gbuf, flags); + bufwrite_buf(gbuf, xs); + bufwrite_buf(gbuf, ys); + + buffree(flags); + buffree(xs); + buffree(ys); +} +static void glyf_build_composite(const glyf_Glyph *g, caryll_Buffer *gbuf) { + bufwrite16b(gbuf, (-1)); + bufwrite16b(gbuf, (int16_t)g->stat.xMin); + bufwrite16b(gbuf, (int16_t)g->stat.yMin); + bufwrite16b(gbuf, (int16_t)g->stat.xMax); + bufwrite16b(gbuf, (int16_t)g->stat.yMax); + for (shapeid_t rj = 0; rj < g->references.length; rj++) { + glyf_ComponentReference *r = &(g->references.items[rj]); + uint16_t flags = + (rj < g->references.length - 1 ? MORE_COMPONENTS + : g->instructionsLength > 0 ? WE_HAVE_INSTRUCTIONS : 0); + bool outputAnchor = r->isAnchored == REF_ANCHOR_CONSOLIDATED; + + union { + uint16_t pointid; + int16_t coord; + } arg1, arg2; + + // flags + if (outputAnchor) { + arg1.pointid = r->outer; + arg2.pointid = r->inner; + if (!(arg1.pointid < 0x100 && arg2.pointid < 0x100)) { flags |= ARG_1_AND_2_ARE_WORDS; } + } else { + flags |= ARGS_ARE_XY_VALUES; + arg1.coord = iVQ.getStill(r->x); + arg2.coord = iVQ.getStill(r->y); + if (!(arg1.coord < 128 && arg1.coord >= -128 && arg2.coord < 128 && + arg2.coord >= -128)) { + flags |= ARG_1_AND_2_ARE_WORDS; + } + } + if (fabs(r->b) > EPSILON || fabs(r->c) > EPSILON) { + flags |= WE_HAVE_A_TWO_BY_TWO; + } else if (fabs(r->a - 1) > EPSILON || fabs(r->d - 1) > EPSILON) { + if (fabs(r->a - r->d) > EPSILON) { + flags |= WE_HAVE_AN_X_AND_Y_SCALE; + } else { + flags |= WE_HAVE_A_SCALE; + } + } + if (r->roundToGrid) flags |= ROUND_XY_TO_GRID; + if (r->useMyMetrics) flags |= USE_MY_METRICS; + bufwrite16b(gbuf, flags); + bufwrite16b(gbuf, r->glyph.index); + if (flags & ARG_1_AND_2_ARE_WORDS) { + bufwrite16b(gbuf, arg1.pointid); + bufwrite16b(gbuf, arg2.pointid); + } else { + bufwrite8(gbuf, arg1.pointid); + bufwrite8(gbuf, arg2.pointid); + } + if (flags & WE_HAVE_A_SCALE) { + bufwrite16b(gbuf, otfcc_to_f2dot14(r->a)); + } else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) { + bufwrite16b(gbuf, otfcc_to_f2dot14(r->a)); + bufwrite16b(gbuf, otfcc_to_f2dot14(r->d)); + } else if (flags & WE_HAVE_A_TWO_BY_TWO) { + bufwrite16b(gbuf, otfcc_to_f2dot14(r->a)); + bufwrite16b(gbuf, otfcc_to_f2dot14(r->b)); + bufwrite16b(gbuf, otfcc_to_f2dot14(r->c)); + bufwrite16b(gbuf, otfcc_to_f2dot14(r->d)); + } + } + if (g->instructionsLength) { + bufwrite16b(gbuf, g->instructionsLength); + if (g->instructions) bufwrite_bytes(gbuf, g->instructionsLength, g->instructions); + } +} +table_GlyfAndLocaBuffers otfcc_buildGlyf(const table_glyf *table, table_head *head, + const otfcc_Options *options) { + caryll_Buffer *bufglyf = bufnew(); + caryll_Buffer *bufloca = bufnew(); + if (table && head) { + caryll_Buffer *gbuf = bufnew(); + uint32_t *loca; + NEW(loca, table->length + 1); + for (glyphid_t j = 0; j < table->length; j++) { + loca[j] = (uint32_t)bufglyf->cursor; + glyf_Glyph *g = table->items[j]; + bufclear(gbuf); + if (g->contours.length > 0) { + glyf_build_simple(g, gbuf); + } else if (g->references.length > 0) { + glyf_build_composite(g, gbuf); + } + // pad extra zeroes + buflongalign(gbuf); + bufwrite_buf(bufglyf, gbuf); + } + loca[table->length] = (uint32_t)bufglyf->cursor; + if (bufglyf->cursor >= 0x20000) { + head->indexToLocFormat = 1; + } else { + head->indexToLocFormat = 0; + } + // write loca table + for (uint32_t j = 0; j <= table->length; j++) { + if (head->indexToLocFormat) { + bufwrite32b(bufloca, loca[j]); + } else { + bufwrite16b(bufloca, loca[j] >> 1); + } + } + buffree(gbuf); + FREE(loca); + } + table_GlyfAndLocaBuffers pair = {bufglyf, bufloca}; + return pair; +} diff --git a/lib/table/glyf/glyf.c b/lib/table/glyf/glyf.c new file mode 100644 index 00000000..7bd7440b --- /dev/null +++ b/lib/table/glyf/glyf.c @@ -0,0 +1,476 @@ +#include "../glyf.h" + +#include "support/util.h" +#include "support/ttinstr/ttinstr.h" + +// point +static void createPoint(glyf_Point *p) { + p->x = iVQ.createStill(0); + p->y = iVQ.createStill(0); + p->onCurve = true; +} +static void copyPoint(glyf_Point *dst, const glyf_Point *src) { + iVQ.copy(&dst->x, &src->x); + iVQ.copy(&dst->y, &src->y); + dst->onCurve = src->onCurve; +} +static void disposePoint(glyf_Point *p) { + iVQ.dispose(&p->x); + iVQ.dispose(&p->y); +} + +caryll_standardValType(glyf_Point, glyf_iPoint, createPoint, copyPoint, disposePoint); + +// contour +caryll_standardVectorImpl(glyf_Contour, glyf_Point, glyf_iPoint, glyf_iContour); +caryll_standardVectorImpl(glyf_ContourList, glyf_Contour, glyf_iContour, glyf_iContourList); + +// ref +static INLINE void initGlyfReference(glyf_ComponentReference *ref) { + ref->glyph = Handle.empty(); + ref->x = iVQ.createStill(0); + ref->y = iVQ.createStill(0); + ref->a = 1; + ref->b = 0; + ref->c = 0; + ref->d = 1; + ref->isAnchored = REF_XY; + ref->inner = ref->outer = 0; + ref->roundToGrid = false; + ref->useMyMetrics = false; +} +static void copyGlyfReference(glyf_ComponentReference *dst, const glyf_ComponentReference *src) { + iVQ.copy(&dst->x, &src->x); + iVQ.copy(&dst->y, &src->y); + Handle.copy(&dst->glyph, &src->glyph); + dst->a = src->a; + dst->b = src->b; + dst->c = src->c; + dst->d = src->d; + dst->isAnchored = src->isAnchored; + dst->inner = src->inner; + dst->outer = src->outer; + dst->roundToGrid = src->roundToGrid; + dst->useMyMetrics = src->useMyMetrics; +} +static INLINE void disposeGlyfReference(glyf_ComponentReference *ref) { + iVQ.dispose(&ref->x); + iVQ.dispose(&ref->y); + Handle.dispose(&ref->glyph); +} +caryll_standardValType(glyf_ComponentReference, glyf_iComponentReference, initGlyfReference, + copyGlyfReference, disposeGlyfReference); +caryll_standardVectorImpl(glyf_ReferenceList, glyf_ComponentReference, glyf_iComponentReference, + glyf_iReferenceList); + +// stem +caryll_standardType(glyf_PostscriptStemDef, glyf_iPostscriptStemDef); +caryll_standardVectorImpl(glyf_StemDefList, glyf_PostscriptStemDef, glyf_iPostscriptStemDef, + glyf_iStemDefList); + +// mask +caryll_standardType(glyf_PostscriptHintMask, glyf_iPostscriptHintMask); +caryll_standardVectorImpl(glyf_MaskList, glyf_PostscriptHintMask, glyf_iPostscriptHintMask, + glyf_iMaskList); + +glyf_Glyph *otfcc_newGlyf_glyph() { + glyf_Glyph *g; + NEW(g); + g->name = NULL; + g->horizontalOrigin = 0; + g->advanceWidth = 0; + g->verticalOrigin = 0; + g->advanceHeight = 0; + + glyf_iContourList.init(&g->contours); + glyf_iReferenceList.init(&g->references); + glyf_iStemDefList.init(&g->stemH); + glyf_iStemDefList.init(&g->stemV); + glyf_iMaskList.init(&g->hintMasks); + glyf_iMaskList.init(&g->contourMasks); + + g->instructionsLength = 0; + g->instructions = NULL; + g->fdSelect = Handle.empty(); + g->yPel = 0; + + g->stat.xMin = 0; + g->stat.xMax = 0; + g->stat.yMin = 0; + g->stat.yMax = 0; + g->stat.nestDepth = 0; + g->stat.nPoints = 0; + g->stat.nContours = 0; + g->stat.nCompositePoints = 0; + g->stat.nCompositeContours = 0; + return g; +} +static void otfcc_deleteGlyf_glyph(glyf_Glyph *g) { + if (!g) return; + sdsfree(g->name); + glyf_iContourList.dispose(&g->contours); + glyf_iReferenceList.dispose(&g->references); + glyf_iStemDefList.dispose(&g->stemH); + glyf_iStemDefList.dispose(&g->stemV); + glyf_iMaskList.dispose(&g->hintMasks); + glyf_iMaskList.dispose(&g->contourMasks); + if (g->instructions) { FREE(g->instructions); } + Handle.dispose(&g->fdSelect); + g->name = NULL; + FREE(g); +} + +static INLINE void initGlyfPtr(glyf_GlyphPtr *g) { + *g = NULL; +} +static void copyGlyfPtr(glyf_GlyphPtr *dst, const glyf_GlyphPtr *src) { + *dst = *src; +} +static INLINE void disposeGlyfPtr(glyf_GlyphPtr *g) { + otfcc_deleteGlyf_glyph(*g); +} +caryll_ElementInterfaceOf(glyf_GlyphPtr) glyf_iGlyphPtr = { + .init = initGlyfPtr, + .copy = copyGlyfPtr, + .dispose = disposeGlyfPtr, +}; +caryll_standardVectorImpl(table_glyf, glyf_GlyphPtr, glyf_iGlyphPtr, table_iGlyf); + +// to json +static void glyf_glyph_dump_contours(glyf_Glyph *g, json_value *target, const GlyfIOContext *ctx) { + if (!g->contours.length) return; + json_value *contours = json_array_new(g->contours.length); + for (shapeid_t k = 0; k < g->contours.length; k++) { + glyf_Contour *c = &(g->contours.items[k]); + json_value *contour = json_array_new(c->length); + for (shapeid_t m = 0; m < c->length; m++) { + json_value *point = json_object_new(4); + json_object_push(point, "x", json_new_VQ(c->items[m].x, ctx->fvar)); + json_object_push(point, "y", json_new_VQ(c->items[m].y, ctx->fvar)); + json_object_push(point, "on", json_boolean_new(c->items[m].onCurve & MASK_ON_CURVE)); + json_array_push(contour, point); + } + json_array_push(contours, preserialize(contour)); + } + json_object_push(target, "contours", contours); +} + +static void glyf_glyph_dump_references(glyf_Glyph *g, json_value *target, + const GlyfIOContext *ctx) { + if (!g->references.length) return; + json_value *references = json_array_new(g->references.length); + for (shapeid_t k = 0; k < g->references.length; k++) { + glyf_ComponentReference *r = &(g->references.items[k]); + json_value *ref = json_object_new(9); + json_object_push(ref, "glyph", + json_string_new_length((uint32_t)sdslen(r->glyph.name), r->glyph.name)); + json_object_push(ref, "x", json_new_VQ(r->x, ctx->fvar)); + json_object_push(ref, "y", json_new_VQ(r->y, ctx->fvar)); + json_object_push(ref, "a", json_new_position(r->a)); + json_object_push(ref, "b", json_new_position(r->b)); + json_object_push(ref, "c", json_new_position(r->c)); + json_object_push(ref, "d", json_new_position(r->d)); + if (r->isAnchored != REF_XY) { + json_object_push(ref, "isAnchored", json_boolean_new(true)); + json_object_push(ref, "inner", json_integer_new(r->inner)); + json_object_push(ref, "outer", json_integer_new(r->outer)); + } + if (r->roundToGrid) { json_object_push(ref, "roundToGrid", json_boolean_new(true)); } + if (r->useMyMetrics) { json_object_push(ref, "useMyMetrics", json_boolean_new(true)); } + json_array_push(references, preserialize(ref)); + } + json_object_push(target, "references", references); +} +static json_value *glyf_glyph_dump_stemdefs(glyf_StemDefList *stems) { + json_value *a = json_array_new(stems->length); + for (shapeid_t j = 0; j < stems->length; j++) { + json_value *stem = json_object_new(3); + json_object_push(stem, "position", json_new_position(stems->items[j].position)); + json_object_push(stem, "width", json_new_position(stems->items[j].width)); + json_array_push(a, stem); + } + return a; +} +static json_value *glyf_glyph_dump_maskdefs(glyf_MaskList *masks, glyf_StemDefList *hh, + glyf_StemDefList *vv) { + json_value *a = json_array_new(masks->length); + for (shapeid_t j = 0; j < masks->length; j++) { + json_value *mask = json_object_new(3); + json_object_push(mask, "contoursBefore", json_integer_new(masks->items[j].contoursBefore)); + json_object_push(mask, "pointsBefore", json_integer_new(masks->items[j].pointsBefore)); + json_value *h = json_array_new(hh->length); + for (shapeid_t k = 0; k < hh->length; k++) { + json_array_push(h, json_boolean_new(masks->items[j].maskH[k])); + } + json_object_push(mask, "maskH", h); + json_value *v = json_array_new(vv->length); + for (shapeid_t k = 0; k < vv->length; k++) { + json_array_push(v, json_boolean_new(masks->items[j].maskV[k])); + } + json_object_push(mask, "maskV", v); + json_array_push(a, mask); + } + return a; +} + +static json_value *glyf_dump_glyph(glyf_Glyph *g, const otfcc_Options *options, + const GlyfIOContext *ctx) { + json_value *glyph = json_object_new(12); + json_object_push(glyph, "advanceWidth", json_new_position(g->advanceWidth)); + if (fabs(g->horizontalOrigin) > 1.0 / 1000.0) { + json_object_push(glyph, "horizontalOrigin", json_new_position(g->horizontalOrigin)); + } + if (ctx->hasVerticalMetrics) { + json_object_push(glyph, "advanceHeight", json_new_position(g->advanceHeight)); + json_object_push(glyph, "verticalOrigin", json_new_position(g->verticalOrigin)); + } + glyf_glyph_dump_contours(g, glyph, ctx); + glyf_glyph_dump_references(g, glyph, ctx); + if (ctx->exportFDSelect) { + json_object_push(glyph, "CFF_fdSelect", json_string_new(g->fdSelect.name)); + json_object_push(glyph, "CFF_CID", json_integer_new(g->cid)); + } + + // hinting data + if (!options->ignore_hints) { + if (g->instructions && g->instructionsLength) { + json_object_push(glyph, "instructions", + dump_ttinstr(g->instructions, g->instructionsLength, options)); + } + if (g->stemH.length) { + json_object_push(glyph, "stemH", preserialize(glyf_glyph_dump_stemdefs(&g->stemH))); + } + if (g->stemV.length) { + json_object_push(glyph, "stemV", preserialize(glyf_glyph_dump_stemdefs(&g->stemV))); + } + if (g->hintMasks.length) { + json_object_push( + glyph, "hintMasks", + preserialize(glyf_glyph_dump_maskdefs(&g->hintMasks, &g->stemH, &g->stemV))); + } + if (g->contourMasks.length) { + json_object_push( + glyph, "contourMasks", + preserialize(glyf_glyph_dump_maskdefs(&g->contourMasks, &g->stemH, &g->stemV))); + } + if (g->yPel) { json_object_push(glyph, "LTSH_yPel", json_integer_new(g->yPel)); } + } + return glyph; +} +void otfcc_dump_glyphorder(const table_glyf *table, json_value *root) { + if (!table) return; + json_value *order = json_array_new(table->length); + for (glyphid_t j = 0; j < table->length; j++) { + json_array_push(order, json_string_new_length((uint32_t)sdslen(table->items[j]->name), + table->items[j]->name)); + } + json_object_push(root, "glyph_order", preserialize(order)); +} +void otfcc_dumpGlyf(const table_glyf *table, json_value *root, const otfcc_Options *options, + const GlyfIOContext *ctx) { + if (!table) return; + loggedStep("glyf") { + json_value *glyf = json_object_new(table->length); + for (glyphid_t j = 0; j < table->length; j++) { + glyf_Glyph *g = table->items[j]; + json_object_push(glyf, g->name, glyf_dump_glyph(g, options, ctx)); + } + json_object_push(root, "glyf", glyf); + if (!options->ignore_glyph_order) otfcc_dump_glyphorder(table, root); + } +} + +// from json +static glyf_Point glyf_parse_point(json_value *pointdump) { + glyf_Point point; + glyf_iPoint.init(&point); + if (!pointdump || pointdump->type != json_object) return point; + for (uint32_t _k = 0; _k < pointdump->u.object.length; _k++) { + char *ck = pointdump->u.object.values[_k].name; + json_value *cv = pointdump->u.object.values[_k].value; + if (strcmp(ck, "x") == 0) { + iVQ.replace(&point.x, json_vqOf(cv, NULL)); + } else if (strcmp(ck, "y") == 0) { + iVQ.replace(&point.y, json_vqOf(cv, NULL)); + } else if (strcmp(ck, "on") == 0) { + point.onCurve = json_boolof(cv); + } + } + return point; +} + +static void glyf_parse_contours(json_value *col, glyf_Glyph *g) { + if (!col) { return; } + shapeid_t nContours = col->u.array.length; + for (shapeid_t j = 0; j < nContours; j++) { + json_value *contourdump = col->u.array.values[j]; + glyf_Contour contour; + glyf_iContour.initCapN(&contour, contourdump && contourdump->type == json_array + ? contourdump->u.array.length + : 1); + if (contourdump && contourdump->type == json_array) { + for (shapeid_t k = 0; k < contourdump->u.array.length; k++) { + glyf_iContour.push(&contour, glyf_parse_point(contourdump->u.array.values[k])); + } + } + glyf_iContourList.push(&g->contours, contour); + } +} + +static glyf_ComponentReference glyf_parse_reference(json_value *refdump) { + json_value *_gname = json_obj_get_type(refdump, "glyph", json_string); + glyf_ComponentReference ref = glyf_iComponentReference.empty(); + if (_gname) { + ref.glyph = Handle.fromName(sdsnewlen(_gname->u.string.ptr, _gname->u.string.length)); + iVQ.replace(&ref.x, json_vqOf(json_obj_get(refdump, "x"), NULL)); + iVQ.replace(&ref.y, json_vqOf(json_obj_get(refdump, "y"), NULL)); + ref.a = json_obj_getnum_fallback(refdump, "a", 1.0); + ref.b = json_obj_getnum_fallback(refdump, "b", 0.0); + ref.c = json_obj_getnum_fallback(refdump, "c", 0.0); + ref.d = json_obj_getnum_fallback(refdump, "d", 1.0); + ref.roundToGrid = json_obj_getbool(refdump, "roundToGrid"); + ref.useMyMetrics = json_obj_getbool(refdump, "useMyMetrics"); + if (json_obj_getbool(refdump, "isAnchored")) { + ref.isAnchored = REF_ANCHOR_XY; + ref.inner = json_obj_getint(refdump, "inner"); + ref.outer = json_obj_getint(refdump, "outer"); + } + } else { + // Invalid glyph references + ref.glyph.name = NULL; + iVQ.replace(&ref.x, iVQ.createStill(0)); + iVQ.replace(&ref.y, iVQ.createStill(0)); + ref.a = 1.0; + ref.b = 0.0; + ref.c = 0.0; + ref.d = 1.0; + ref.roundToGrid = false; + ref.useMyMetrics = false; + } + return ref; +} +static void glyf_parse_references(json_value *col, glyf_Glyph *g) { + if (!col) { return; } + for (shapeid_t j = 0; j < col->u.array.length; j++) { + glyf_iReferenceList.push(&g->references, glyf_parse_reference(col->u.array.values[j])); + } +} + +static void makeInstrsForGlyph(void *_g, uint8_t *instrs, uint32_t len) { + glyf_Glyph *g = (glyf_Glyph *)_g; + g->instructionsLength = len; + g->instructions = instrs; +} +static void wrongInstrsForGlyph(void *_g, char *reason, int pos) { + glyf_Glyph *g = (glyf_Glyph *)_g; + fprintf(stderr, "[OTFCC] TrueType instructions parse error : %s, at %d in /%s\n", reason, pos, + g->name); +} + +static void parse_stems(json_value *sd, glyf_StemDefList *stems) { + if (!sd) return; + for (shapeid_t j = 0; j < sd->u.array.length; j++) { + json_value *s = sd->u.array.values[j]; + if (s->type != json_object) continue; + glyf_PostscriptStemDef sdef; + sdef.map = 0; + sdef.position = json_obj_getnum(s, "position"); + sdef.width = json_obj_getnum(s, "width"); + glyf_iStemDefList.push(stems, sdef); + } +} +static void parse_maskbits(bool *arr, json_value *bits) { + if (!bits) { + for (shapeid_t j = 0; j < 0x100; j++) { + arr[j] = false; + } + } else { + for (shapeid_t j = 0; j < 0x100 && j < bits->u.array.length; j++) { + json_value *b = bits->u.array.values[j]; + switch (b->type) { + case json_boolean: + arr[j] = b->u.boolean; + break; + case json_integer: + arr[j] = b->u.integer; + break; + case json_double: + arr[j] = b->u.dbl; + break; + default: + arr[j] = false; + } + } + } +} +static void parse_masks(json_value *md, glyf_MaskList *masks) { + if (!md) return; + for (shapeid_t j = 0; j < md->u.array.length; j++) { + json_value *m = md->u.array.values[j]; + if (m->type != json_object) continue; + + glyf_PostscriptHintMask mask; + mask.pointsBefore = json_obj_getint(m, "pointsBefore"); + mask.contoursBefore = json_obj_getint(m, "contoursBefore"); + parse_maskbits(&(mask.maskH[0]), json_obj_get_type(m, "maskH", json_array)); + parse_maskbits(&(mask.maskV[0]), json_obj_get_type(m, "maskV", json_array)); + glyf_iMaskList.push(masks, mask); + } +} + +static glyf_Glyph *otfcc_glyf_parse_glyph(json_value *glyphdump, otfcc_GlyphOrderEntry *order_entry, + const otfcc_Options *options) { + glyf_Glyph *g = otfcc_newGlyf_glyph(); + g->name = sdsdup(order_entry->name); + g->advanceWidth = json_obj_getnum(glyphdump, "advanceWidth"); + g->horizontalOrigin = json_obj_getnum(glyphdump, "horizontalOrigin"); + g->advanceHeight = json_obj_getnum(glyphdump, "advanceHeight"); + g->verticalOrigin = json_obj_getnum(glyphdump, "verticalOrigin"); + glyf_parse_contours(json_obj_get_type(glyphdump, "contours", json_array), g); + glyf_parse_references(json_obj_get_type(glyphdump, "references", json_array), g); + if (!options->ignore_hints) { + parse_ttinstr(json_obj_get(glyphdump, "instructions"), g, makeInstrsForGlyph, + wrongInstrsForGlyph); + parse_stems(json_obj_get_type(glyphdump, "stemH", json_array), &g->stemH); + parse_stems(json_obj_get_type(glyphdump, "stemV", json_array), &g->stemV); + parse_masks(json_obj_get_type(glyphdump, "hintMasks", json_array), &g->hintMasks); + parse_masks(json_obj_get_type(glyphdump, "contourMasks", json_array), &g->contourMasks); + g->yPel = json_obj_getint(glyphdump, "LTSH_yPel"); + } + // Glyph data of other tables + g->fdSelect = Handle.fromName(json_obj_getsds(glyphdump, "CFF_fdSelect")); + if (!g->yPel) { g->yPel = json_obj_getint(glyphdump, "yPel"); } + return g; +} + +table_glyf *otfcc_parseGlyf(const json_value *root, otfcc_GlyphOrder *glyph_order, + const otfcc_Options *options) { + if (root->type != json_object || !glyph_order) return NULL; + table_glyf *glyf = NULL; + json_value *table; + if ((table = json_obj_get_type(root, "glyf", json_object))) { + loggedStep("glyf") { + glyphid_t numGlyphs = table->u.object.length; + glyf = table_iGlyf.createN(numGlyphs); + for (glyphid_t j = 0; j < numGlyphs; j++) { + sds gname = sdsnewlen(table->u.object.values[j].name, + table->u.object.values[j].name_length); + json_value *glyphdump = table->u.object.values[j].value; + otfcc_GlyphOrderEntry *order_entry = NULL; + HASH_FIND(hhName, glyph_order->byName, gname, sdslen(gname), order_entry); + if (glyphdump->type == json_object && order_entry && + !glyf->items[order_entry->gid]) { + glyf->items[order_entry->gid] = + otfcc_glyf_parse_glyph(glyphdump, order_entry, options); + } + json_value_free(glyphdump); + json_value *v = json_null_new(); + v->parent = table; + table->u.object.values[j].value = v; + sdsfree(gname); + } + } + return glyf; + } + return NULL; +} diff --git a/lib/table/glyf/read.c b/lib/table/glyf/read.c new file mode 100644 index 00000000..1cabae8d --- /dev/null +++ b/lib/table/glyf/read.c @@ -0,0 +1,690 @@ +#include "../glyf.h" + +#include "support/util.h" +#include "support/ttinstr/ttinstr.h" + +static glyf_Point *next_point(glyf_ContourList *contours, shapeid_t *cc, shapeid_t *cp) { + if (*cp >= contours->items[*cc].length) { + *cp = 0; + *cc += 1; + } + return &contours->items[*cc].items[(*cp)++]; +} + +static glyf_Glyph *otfcc_read_simple_glyph(font_file_pointer start, shapeid_t numberOfContours, + const otfcc_Options *options) { + glyf_Glyph *g = otfcc_newGlyf_glyph(); + glyf_ContourList *contours = &g->contours; + + shapeid_t pointsInGlyph = 0; + for (shapeid_t j = 0; j < numberOfContours; j++) { + shapeid_t lastPointInCurrentContour = read_16u(start + 2 * j); + glyf_Contour contour; + glyf_iContour.init(&contour); + glyf_iContour.fill(&contour, lastPointInCurrentContour - pointsInGlyph + 1); + glyf_iContourList.push(contours, contour); + pointsInGlyph = lastPointInCurrentContour + 1; + } + uint16_t instructionLength = read_16u(start + 2 * numberOfContours); + uint8_t *instructions = NULL; + if (instructionLength > 0) { + NEW(instructions, instructionLength); + memcpy(instructions, start + 2 * numberOfContours + 2, sizeof(uint8_t) * instructionLength); + } + g->instructionsLength = instructionLength; + g->instructions = instructions; + + // read flags + // There are repeating entries in the flags list, we will fill out the + // result + font_file_pointer flags; + NEW(flags, pointsInGlyph); + font_file_pointer flagStart = start + 2 * numberOfContours + 2 + instructionLength; + shapeid_t flagsReadSofar = 0; + shapeid_t flagBytesReadSofar = 0; + + shapeid_t currentContour = 0; + shapeid_t currentContourPointIndex = 0; + while (flagsReadSofar < pointsInGlyph) { + uint8_t flag = flagStart[flagBytesReadSofar]; + flags[flagsReadSofar] = flag; + flagBytesReadSofar += 1; + flagsReadSofar += 1; + next_point(contours, ¤tContour, ¤tContourPointIndex)->onCurve = + (flag & GLYF_FLAG_ON_CURVE); + if (flag & GLYF_FLAG_REPEAT) { // repeating flag + uint8_t repeat = flagStart[flagBytesReadSofar]; + flagBytesReadSofar += 1; + for (uint8_t j = 0; j < repeat; j++) { + flags[flagsReadSofar + j] = flag; + next_point(contours, ¤tContour, ¤tContourPointIndex)->onCurve = + (flag & GLYF_FLAG_ON_CURVE); + } + flagsReadSofar += repeat; + } + } + + // read X coordinates + font_file_pointer coordinatesStart = flagStart + flagBytesReadSofar; + uint32_t coordinatesOffset = 0; + shapeid_t coordinatesRead = 0; + currentContour = 0; + currentContourPointIndex = 0; + while (coordinatesRead < pointsInGlyph) { + uint8_t flag = flags[coordinatesRead]; + int16_t x; + if (flag & GLYF_FLAG_X_SHORT) { + x = (flag & GLYF_FLAG_POSITIVE_X ? 1 : -1) * + read_8u(coordinatesStart + coordinatesOffset); + coordinatesOffset += 1; + } else { + if (flag & GLYF_FLAG_SAME_X) { + x = 0; + } else { + x = read_16s(coordinatesStart + coordinatesOffset); + coordinatesOffset += 2; + } + } + iVQ.replace(&(next_point(contours, ¤tContour, ¤tContourPointIndex)->x), + iVQ.createStill(x)); + coordinatesRead += 1; + } + // read Y, identical to X + coordinatesRead = 0; + currentContour = 0; + currentContourPointIndex = 0; + while (coordinatesRead < pointsInGlyph) { + uint8_t flag = flags[coordinatesRead]; + int16_t y; + if (flag & GLYF_FLAG_Y_SHORT) { + y = (flag & GLYF_FLAG_POSITIVE_Y ? 1 : -1) * + read_8u(coordinatesStart + coordinatesOffset); + coordinatesOffset += 1; + } else { + if (flag & GLYF_FLAG_SAME_Y) { + y = 0; + } else { + y = read_16s(coordinatesStart + coordinatesOffset); + coordinatesOffset += 2; + } + } + iVQ.replace(&(next_point(contours, ¤tContour, ¤tContourPointIndex)->y), + iVQ.createStill(y)); + coordinatesRead += 1; + } + FREE(flags); + + // turn deltas to absolute coordiantes + VQ cx = iVQ.neutral(), cy = iVQ.neutral(); + for (shapeid_t j = 0; j < numberOfContours; j++) { + for (shapeid_t k = 0; k < contours->items[j].length; k++) { + glyf_Point *z = &contours->items[j].items[k]; + iVQ.inplacePlus(&cx, z->x); + iVQ.inplacePlus(&cy, z->y); + iVQ.copyReplace(&z->x, cx); + iVQ.copyReplace(&z->y, cy); + } + glyf_iContour.shrinkToFit(&contours->items[j]); + } + glyf_iContourList.shrinkToFit(contours); + iVQ.dispose(&cx), iVQ.dispose(&cy); + return g; +} + +static glyf_Glyph *otfcc_read_composite_glyph(font_file_pointer start, + const otfcc_Options *options) { + glyf_Glyph *g = otfcc_newGlyf_glyph(); + + // pass 1, read references quantity + uint16_t flags = 0; + uint32_t offset = 0; + bool glyphHasInstruction = false; + do { + flags = read_16u(start + offset); + glyphid_t index = read_16u(start + offset + 2); + + glyf_ComponentReference ref = glyf_iComponentReference.empty(); + ref.glyph = Handle.fromIndex(index); + + offset += 4; // flags & index + if (flags & ARGS_ARE_XY_VALUES) { + ref.isAnchored = REF_XY; + if (flags & ARG_1_AND_2_ARE_WORDS) { + ref.x = iVQ.createStill(read_16s(start + offset)); + ref.y = iVQ.createStill(read_16s(start + offset + 2)); + offset += 4; + } else { + ref.x = iVQ.createStill(read_8s(start + offset)); + ref.y = iVQ.createStill(read_8s(start + offset + 1)); + offset += 2; + } + } else { + ref.isAnchored = REF_ANCHOR_ANCHOR; + if (flags & ARG_1_AND_2_ARE_WORDS) { + ref.outer = read_16u(start + offset); + ref.inner = read_16u(start + offset + 2); + offset += 4; + } else { + ref.outer = read_8u(start + offset); + ref.inner = read_8u(start + offset + 1); + offset += 2; + } + } + if (flags & WE_HAVE_A_SCALE) { + ref.a = ref.d = otfcc_from_f2dot14(read_16s(start + offset)); + offset += 2; + } else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) { + ref.a = otfcc_from_f2dot14(read_16s(start + offset)); + ref.d = otfcc_from_f2dot14(read_16s(start + offset + 2)); + offset += 4; + } else if (flags & WE_HAVE_A_TWO_BY_TWO) { + ref.a = otfcc_from_f2dot14(read_16s(start + offset)); + ref.b = otfcc_from_f2dot14(read_16s(start + offset + 2)); + ref.c = otfcc_from_f2dot14(read_16s(start + offset + 4)); + ref.d = otfcc_from_f2dot14(read_16s(start + offset + 2)); + offset += 8; + } + ref.roundToGrid = flags & ROUND_XY_TO_GRID; + ref.useMyMetrics = flags & USE_MY_METRICS; + if (flags & WE_HAVE_INSTRUCTIONS) { glyphHasInstruction = true; } + glyf_iReferenceList.push(&g->references, ref); + } while (flags & MORE_COMPONENTS); + + if (glyphHasInstruction) { + uint16_t instructionLength = read_16u(start + offset); + font_file_pointer instructions = NULL; + if (instructionLength > 0) { + NEW(instructions, instructionLength); + memcpy(instructions, start + offset + 2, sizeof(uint8_t) * instructionLength); + } + g->instructionsLength = instructionLength; + g->instructions = instructions; + } else { + g->instructionsLength = 0; + g->instructions = NULL; + } + + return g; +} + +static glyf_Glyph *otfcc_read_glyph(font_file_pointer data, uint32_t offset, + const otfcc_Options *options) { + font_file_pointer start = data + offset; + int16_t numberOfContours = read_16u(start); + glyf_Glyph *g; + if (numberOfContours > 0) { + g = otfcc_read_simple_glyph(start + 10, numberOfContours, options); + } else { + g = otfcc_read_composite_glyph(start + 10, options); + } + g->stat.xMin = read_16s(start + 2); + g->stat.yMin = read_16s(start + 4); + g->stat.xMax = read_16s(start + 6); + g->stat.yMax = read_16s(start + 8); + return g; +} + +// common states of tuple polymorphizer + +typedef struct { + table_fvar *fvar; + uint16_t dimensions; + uint16_t sharedTupleCount; + f2dot14 *sharedTuples; + uint8_t coordDimensions; + bool allowIUP; + shapeid_t nPhantomPoints; +} TuplePolymorphizerCtx; + +// GVAR header +#pragma pack(1) +struct GVARHeader { + uint16_t majorVersion; + uint16_t minorVersion; + uint16_t axisCount; + uint16_t sharedTupleCount; + uint32_t sharedTuplesOffset; + uint16_t glyphCount; + uint16_t flags; + uint32_t glyphVariationDataArrayOffset; +}; + +struct TupleVariationHeader { + uint16_t variationDataSize; + uint16_t tupleIndex; +}; +struct GlyphVariationData { + uint16_t tupleVariationCount; + uint16_t dataOffset; + struct TupleVariationHeader tvhs[]; +}; +#pragma pack() + +#define GVAR_OFFSETS_ARE_LONG 1 +#define EMBEDDED_PEAK_TUPLE 0x8000 +#define INTERMEDIATE_REGION 0x4000 +#define PRIVATE_POINT_NUMBERS 0x2000 +#define TUPLE_INDEX_MASK 0xFFF + +static INLINE struct TupleVariationHeader *nextTVH(struct TupleVariationHeader *currentHeader, + const TuplePolymorphizerCtx *ctx) { + uint32_t bump = 2 * sizeof(uint16_t); + uint16_t tupleIndex = be16(currentHeader->tupleIndex); + if (tupleIndex & EMBEDDED_PEAK_TUPLE) bump += ctx->dimensions * sizeof(f2dot14); + if (tupleIndex & INTERMEDIATE_REGION) bump += 2 * ctx->dimensions * sizeof(f2dot14); + return (struct TupleVariationHeader *)((font_file_pointer)currentHeader + bump); +} + +#define POINT_COUNT_IS_WORD 0x80 +#define POINT_COUNT_LONG_MASK 0x7FFF +#define POINT_RUN_COUNT_MASK 0x7F +#define POINTS_ARE_WORDS 0x80 + +static INLINE font_file_pointer parsePointNumbers(font_file_pointer data, shapeid_t **pointIndeces, + shapeid_t *pc, shapeid_t totalPoints) { + uint16_t nPoints = 0; + uint8_t firstByte = *data; + if (firstByte & POINT_COUNT_IS_WORD) { + nPoints = (data[0] << 8 | data[1]) & POINT_COUNT_LONG_MASK; + data += 2; + } else { + nPoints = firstByte; + data++; + } + if (nPoints > 0) { + struct { + shapeid_t length; + bool wide; + } run = {0, false}; + shapeid_t filled = 0; + shapeid_t jPoint = 0; + NEW_CLEAN_N(*pointIndeces, nPoints); + while (filled < nPoints) { + if (run.length == 0) { + uint8_t runHeader = *data++; + run.wide = !!(runHeader & POINTS_ARE_WORDS); + run.length = (runHeader & POINT_RUN_COUNT_MASK) + 1; + } + int16_t pointNumber = jPoint; + if (run.wide) { + pointNumber += *((uint16_t *)data); + data += 2; + } else { + pointNumber += *data++; + } + (*pointIndeces)[filled] = pointNumber; + filled++; + jPoint = pointNumber; + --run.length; + } + *pc = nPoints; + } else { + NEW_CLEAN_N(*pointIndeces, totalPoints); + for (shapeid_t j = 0; j < totalPoints; j++) { + (*pointIndeces)[j] = j; + } + *pc = totalPoints; + } + + return data; +} + +#define DELTAS_ARE_ZERO 0x80 +#define DELTAS_ARE_WORDS 0x40 +#define DELTA_RUN_COUNT_MASK 0x3F + +static INLINE font_file_pointer readPackedDelta(font_file_pointer data, shapeid_t nPoints, + pos_t *deltas) { + struct { + shapeid_t length; + bool wide; + bool zero; + } run = {0, false, false}; + shapeid_t filled = 0; + while (filled < nPoints) { + int16_t delta = 0; + if (run.length == 0) { + uint8_t runHeader = *data++; + run.zero = runHeader & DELTAS_ARE_ZERO; + run.wide = runHeader & DELTAS_ARE_WORDS; + run.length = (runHeader & DELTA_RUN_COUNT_MASK) + 1; + } + if (!run.zero) { + if (run.wide) { + delta = (int16_t)be16(*(uint16_t *)data); + data += 2; + } else { + delta = (int8_t)*data; + data++; + } + } + deltas[filled] = (pos_t)delta; + filled++; + --run.length; + } + return data; +} + +typedef VQ *(*CoordPartGetter)(glyf_Point *z); + +VQ *getX(glyf_Point *z) { + return &z->x; +} +VQ *getY(glyf_Point *z) { + return &z->y; +} + +static INLINE void fillTheGaps(shapeid_t jMin, shapeid_t jMax, vq_Segment *nudges, + glyf_Point **glyphRefs, CoordPartGetter getter) { + for (shapeid_t j = jMin; j < jMax; j++) { + if (nudges[j].val.delta.touched) continue; + // get next knot + shapeid_t jNext = j; + while (!nudges[jNext].val.delta.touched) { + if (jNext == jMax - 1) { + jNext = jMin; + } else { + jNext += 1; + } + if (jNext == j) break; + } + // get pre knot + shapeid_t jPrev = j; + while (!nudges[jPrev].val.delta.touched) { + if (jPrev == jMin) { + jPrev = jMax - 1; + } else { + jPrev -= 1; + } + if (jPrev == j) break; + } + if (nudges[jNext].val.delta.touched && nudges[jPrev].val.delta.touched) { + f16dot16 untouchJ = otfcc_to_fixed(getter(glyphRefs[j])->kernel); + f16dot16 untouchPrev = otfcc_to_fixed(getter(glyphRefs[jPrev])->kernel); + f16dot16 untouchNext = otfcc_to_fixed(getter(glyphRefs[jNext])->kernel); + f16dot16 deltaPrev = otfcc_to_fixed(nudges[jPrev].val.delta.quantity); + f16dot16 deltaNext = otfcc_to_fixed(nudges[jNext].val.delta.quantity); + + f16dot16 uMin = untouchPrev; + f16dot16 uMax = untouchNext; + f16dot16 dMin = deltaPrev; + f16dot16 dMax = deltaNext; + if (untouchPrev > untouchNext) { + uMin = untouchNext; + uMax = untouchPrev; + dMin = deltaNext; + dMax = deltaPrev; + } + if (untouchJ <= uMin) { + nudges[j].val.delta.quantity = otfcc_from_fixed(dMin); + } else if (untouchJ >= uMax) { + nudges[j].val.delta.quantity = otfcc_from_fixed(dMax); + } else { + nudges[j].val.delta.quantity = + otfcc_from_fixed(otfcc_f1616_muldiv(dMax - dMin, untouchJ - uMin, uMax - uMin)); + } + } + } +} + +static void applyCoords(const shapeid_t totalPoints, glyf_Glyph *glyph, + glyf_Point **glyphRefs, // target + const shapeid_t nTouchedPoints, const pos_t *tupleDelta, + const shapeid_t *points, + const vq_Region *r, // data + CoordPartGetter getter) { + vq_Segment *nudges; + NEW_CLEAN_N(nudges, totalPoints); + for (shapeid_t j = 0; j < totalPoints; j++) { + nudges[j].type = VQ_DELTA; + nudges[j].val.delta.touched = false; + nudges[j].val.delta.quantity = 0; + nudges[j].val.delta.region = r; + } + for (shapeid_t j = 0; j < nTouchedPoints; j++) { + if (points[j] >= totalPoints) continue; + nudges[points[j]].val.delta.touched = true; + nudges[points[j]].val.delta.quantity += tupleDelta[j]; + } + // fill the gaps + shapeid_t jFirst = 0; + foreach (glyf_Contour *c, glyph->contours) { + fillTheGaps(jFirst, jFirst + c->length, nudges, glyphRefs, getX); + jFirst += c->length; + } + for (shapeid_t j = 0; j < totalPoints; j++) { + if (!nudges[j].val.delta.quantity && nudges[j].val.delta.touched) continue; + VQ *coordinatePart = getter(glyphRefs[j]); + vq_iSegList.push(&(coordinatePart->shift), nudges[j]); + } + FREE(nudges); +} + +static INLINE void applyPolymorphism(const shapeid_t totalPoints, glyf_GlyphPtr glyph, // target + const shapeid_t nTouchedPoints, const shapeid_t *points, + const pos_t *deltaX, const pos_t *deltaY, + const vq_Region *r // delta data +) { + glyf_Point **glyphRefs; + NEW_CLEAN_N(glyphRefs, totalPoints); + { + shapeid_t j = 0; + foreach (glyf_Contour *c, glyph->contours) { + foreach (glyf_Point *g, *c) { glyphRefs[j++] = g; } + } + foreach (glyf_ComponentReference *r, glyph->references) { + // in glyf_ComponentReference, we also have a X and a Y entry + // so a trick of conversion + glyphRefs[j++] = (glyf_Point *)&(r->x); + } + } + applyCoords(totalPoints, glyph, glyphRefs, nTouchedPoints, deltaX, points, r, getX); + applyCoords(totalPoints, glyph, glyphRefs, nTouchedPoints, deltaY, points, r, getY); + FREE(glyphRefs); +} + +static vq_Region *createRegionFromTuples(uint16_t dimensions, f2dot14 *peak, f2dot14 *start, + f2dot14 *end) { + vq_Region *r = vq_createRegion(dimensions); + for (uint16_t d = 0; d < dimensions; d++) { + pos_t peakVal = otfcc_from_f2dot14(be16(peak[d])); + vq_AxisSpan span = {peakVal <= 0 ? -1 : 0, peakVal, peakVal >= 0 ? 1 : 0}; + if (start && end) { + span.start = otfcc_from_f2dot14(be16(start[d])); + span.end = otfcc_from_f2dot14(be16(end[d])); + } + r->spans[d] = span; + } + return r; +} + +static INLINE void polymorphizeGlyph(glyphid_t gid, glyf_GlyphPtr glyph, + const TuplePolymorphizerCtx *ctx, + struct GlyphVariationData *gvd, const otfcc_Options *options) { + + shapeid_t totalPoints = 0; + foreach (glyf_Contour *c, glyph->contours) { totalPoints += c->length; } + totalPoints += glyph->references.length; + shapeid_t totalDeltaEntries = totalPoints + ctx->nPhantomPoints; + + uint16_t nTuples = be16(gvd->tupleVariationCount) & 0xFFF; + struct TupleVariationHeader *tvh = gvd->tvhs; + + bool hasSharedPointNumbers = be16(gvd->tupleVariationCount) & 0x8000; + shapeid_t sharedPointCount = 0; + shapeid_t *sharedPointIndeces = NULL; + font_file_pointer data = (font_file_pointer)gvd + be16(gvd->dataOffset); + if (hasSharedPointNumbers) { + data = parsePointNumbers(data, &sharedPointIndeces, &sharedPointCount, totalDeltaEntries); + } + + size_t tsdStart = 0; + + for (uint16_t j = 0; j < nTuples; j++) { + + // Tuple options + shapeid_t tupleIndex = be16(tvh->tupleIndex) & TUPLE_INDEX_MASK; + bool hasEmbeddedPeak = be16(tvh->tupleIndex) & EMBEDDED_PEAK_TUPLE; + bool hasIntermediate = be16(tvh->tupleIndex) & INTERMEDIATE_REGION; + + // Peak tuple + f2dot14 *peak = NULL; + if (hasEmbeddedPeak) { + peak = (f2dot14 *)(((font_file_pointer)tvh) + 4); + } else { + peak = ctx->sharedTuples + ctx->dimensions * tupleIndex; + } + + // Intermediate tuple -- if present + f2dot14 *start = NULL; + f2dot14 *end = NULL; + if (hasIntermediate) { + start = (f2dot14 *)(((font_file_pointer)tvh) + 4 + + 2 * (hasEmbeddedPeak ? 1 : 0) * ctx->dimensions); + end = (f2dot14 *)(((font_file_pointer)tvh) + 4 + + 2 * (hasEmbeddedPeak ? 2 : 1) * ctx->dimensions); + } + + const vq_Region *r = table_iFvar.registerRegion( + ctx->fvar, createRegionFromTuples(ctx->dimensions, peak, start, end)); + + // Pointer of tuple serialized data + font_file_pointer tsd = data + tsdStart; + // Point number term + shapeid_t nPoints = sharedPointCount; + shapeid_t *pointIndeces = sharedPointIndeces; + + if (be16(tvh->tupleIndex) & PRIVATE_POINT_NUMBERS) { + shapeid_t privatePointCount = 0; + shapeid_t *privatePointNumbers = NULL; + tsd = + parsePointNumbers(tsd, &privatePointNumbers, &privatePointCount, totalDeltaEntries); + nPoints = privatePointCount; + pointIndeces = privatePointNumbers; + } + if (pointIndeces) { + // Delta term + pos_t *deltaX = 0; + pos_t *deltaY = 0; + NEW_CLEAN_N(deltaX, nPoints); + NEW_CLEAN_N(deltaY, nPoints); + + tsd = readPackedDelta(tsd, nPoints, deltaX); + tsd = readPackedDelta(tsd, nPoints, deltaY); + + // Do polymorphize + applyPolymorphism(totalPoints, glyph, nPoints, pointIndeces, deltaX, deltaY, r); + FREE(deltaX); + FREE(deltaY); + } + // Cleanup + if (be16(tvh->tupleIndex) & PRIVATE_POINT_NUMBERS) { FREE(pointIndeces); } + tsdStart += be16(tvh->variationDataSize); + + tvh = nextTVH(tvh, ctx); + } + FREE(sharedPointIndeces); +} + +// NOTE: for polymorphize, we would +// TODO: polymorphize advanceWidth, verticalOrigin and advanceHeight +static INLINE void polymorphize(const otfcc_Packet packet, const otfcc_Options *options, + table_glyf *glyf, const GlyfIOContext *ctx) { + if (!ctx->fvar || !ctx->fvar->axes.length) return; + FOR_TABLE('gvar', table) { + font_file_pointer data = table.data; + if (table.length < sizeof(struct GVARHeader)) return; + struct GVARHeader *header = (struct GVARHeader *)data; + if (be16(header->axisCount) != ctx->fvar->axes.length) { + logWarning("Axes number in GVAR and FVAR are inequal"); + return; + }; + for (glyphid_t j = 0; j < glyf->length; j++) { + TuplePolymorphizerCtx tpctx = {.fvar = ctx->fvar, + .dimensions = ctx->fvar->axes.length, + .nPhantomPoints = ctx->nPhantomPoints, + .sharedTupleCount = be16(header->sharedTupleCount), + .sharedTuples = + (f2dot14 *)(data + be32(header->sharedTuplesOffset)), + .coordDimensions = 2, + .allowIUP = glyf->items[j]->contours.length > 0}; + uint32_t glyphVariationDataOffset = 0; + if (be16(header->flags) & GVAR_OFFSETS_ARE_LONG) { + glyphVariationDataOffset = + be32(((uint32_t *)(data + sizeof(struct GVARHeader)))[j]); + } else { + glyphVariationDataOffset = + 2 * be16(((uint16_t *)(data + sizeof(struct GVARHeader)))[j]); + } + struct GlyphVariationData *gvd = + (struct GlyphVariationData *)(data + be32(header->glyphVariationDataArrayOffset) + + glyphVariationDataOffset); + polymorphizeGlyph(j, glyf->items[j], &tpctx, gvd, options); + } + } +} + +table_glyf *otfcc_readGlyf(const otfcc_Packet packet, const otfcc_Options *options, + const GlyfIOContext *ctx) { + uint32_t *offsets = NULL; + table_glyf *glyf = NULL; + + NEW_CLEAN_N(offsets, (ctx->numGlyphs + 1)); + if (!offsets) goto ABSENT; + bool foundLoca = false; + + // read loca + FOR_TABLE('loca', table) { + font_file_pointer data = table.data; + uint32_t length = table.length; + if (length < 2 * ctx->numGlyphs + 2) goto LOCA_CORRUPTED; + for (uint32_t j = 0; j < ctx->numGlyphs + 1; j++) { + if (ctx->locaIsLong) { + offsets[j] = read_32u(data + j * 4); + } else { + offsets[j] = read_16u(data + j * 2) * 2; + } + if (j > 0 && offsets[j] < offsets[j - 1]) goto LOCA_CORRUPTED; + } + foundLoca = true; + break; + LOCA_CORRUPTED: + logWarning("table 'loca' corrupted.\n"); + if (offsets) { FREE(offsets), offsets = NULL; } + continue; + } + if (!foundLoca) goto ABSENT; + + // read glyf + FOR_TABLE('glyf', table) { + font_file_pointer data = table.data; + uint32_t length = table.length; + if (length < offsets[ctx->numGlyphs]) goto GLYF_CORRUPTED; + + glyf = table_iGlyf.create(); + + for (glyphid_t j = 0; j < ctx->numGlyphs; j++) { + if (offsets[j] < offsets[j + 1]) { // non-space glyph + table_iGlyf.push(glyf, otfcc_read_glyph(data, offsets[j], options)); + } else { // space glyph + table_iGlyf.push(glyf, otfcc_newGlyf_glyph()); + } + } + goto PRESENT; + GLYF_CORRUPTED: + logWarning("table 'glyf' corrupted.\n"); + if (glyf) { DELETE(table_iGlyf.free, glyf), glyf = NULL; } + } + goto ABSENT; + +PRESENT: + if (offsets) { FREE(offsets), offsets = NULL; } + // We have glyf table read. Do polymorphize + polymorphize(packet, options, glyf, ctx); + return glyf; + +ABSENT: + if (offsets) { FREE(offsets), offsets = NULL; } + if (glyf) { FREE(glyf), glyf = NULL; } + return NULL; +} diff --git a/lib/table/hhea.c b/lib/table/hhea.c index 795fd99c..365902b6 100644 --- a/lib/table/hhea.c +++ b/lib/table/hhea.c @@ -25,7 +25,7 @@ table_hhea *otfcc_readHhea(const otfcc_Packet packet, const otfcc_Options *optio hhea->ascender = read_16u(data + 4); hhea->descender = read_16u(data + 6); hhea->lineGap = read_16u(data + 8); - hhea->advanceWithMax = read_16u(data + 10); + hhea->advanceWidthMax = read_16u(data + 10); hhea->minLeftSideBearing = read_16u(data + 12); hhea->minRightSideBearing = read_16u(data + 14); hhea->xMaxExtent = read_16u(data + 16); @@ -52,7 +52,7 @@ void otfcc_dumpHhea(const table_hhea *table, json_value *root, const otfcc_Optio json_object_push(hhea, "ascender", json_integer_new(table->ascender)); json_object_push(hhea, "descender", json_integer_new(table->descender)); json_object_push(hhea, "lineGap", json_integer_new(table->lineGap)); - json_object_push(hhea, "advanceWithMax", json_integer_new(table->advanceWithMax)); + json_object_push(hhea, "advanceWidthMax", json_integer_new(table->advanceWidthMax)); json_object_push(hhea, "minLeftSideBearing", json_integer_new(table->minLeftSideBearing)); json_object_push(hhea, "minRightSideBearing", json_integer_new(table->minRightSideBearing)); json_object_push(hhea, "xMaxExtent", json_integer_new(table->xMaxExtent)); @@ -72,7 +72,7 @@ table_hhea *otfcc_parseHhea(const json_value *root, const otfcc_Options *options hhea->ascender = json_obj_getnum_fallback(table, "ascender", 0); hhea->descender = json_obj_getnum_fallback(table, "descender", 0); hhea->lineGap = json_obj_getnum_fallback(table, "lineGap", 0); - hhea->advanceWithMax = json_obj_getnum_fallback(table, "advanceWithMax", 0); + hhea->advanceWidthMax = json_obj_getnum_fallback(table, "advanceWidthMax", 0); hhea->minLeftSideBearing = json_obj_getnum_fallback(table, "minLeftSideBearing", 0); hhea->minRightSideBearing = json_obj_getnum_fallback(table, "minRightSideBearing", 0); hhea->xMaxExtent = json_obj_getnum_fallback(table, "xMaxExtent", 0); @@ -91,7 +91,7 @@ caryll_Buffer *otfcc_buildHhea(const table_hhea *hhea, const otfcc_Options *opti bufwrite16b(buf, hhea->ascender); bufwrite16b(buf, hhea->descender); bufwrite16b(buf, hhea->lineGap); - bufwrite16b(buf, hhea->advanceWithMax); + bufwrite16b(buf, hhea->advanceWidthMax); bufwrite16b(buf, hhea->minLeftSideBearing); bufwrite16b(buf, hhea->minRightSideBearing); bufwrite16b(buf, hhea->xMaxExtent); diff --git a/lib/table/vdmx/funcs.c b/lib/table/vdmx/funcs.c new file mode 100644 index 00000000..a53afc24 --- /dev/null +++ b/lib/table/vdmx/funcs.c @@ -0,0 +1,150 @@ +#include "../VDMX.h" + +#include "support/util.h" +#include "bk/bkgraph.h" + +table_VDMX *otfcc_readVDMX(const otfcc_Packet packet, const otfcc_Options *options) { + table_VDMX *vdmx = NULL; + FOR_TABLE('VDMX', table) { + if (table.length < 6) goto FAIL; + uint16_t version = read_16u(table.data + 0); + uint16_t numRatios = read_16u(table.data + 4); + if (table.length < 6 + 6 * numRatios) goto FAIL; + + vdmx = table_iVDMX.create(); + vdmx->version = version; + for (shapeid_t g = 0; g < numRatios; g++) { + const size_t ratioRangeOffset = 6 + 4 * g; + const size_t offsetOffset = 6 + 4 * numRatios + 2 * g; + vdmx_RatioRange r; + vdmx_iRatioRange.init(&r); + r.bCharset = read_8u(table.data + ratioRangeOffset + 0); + r.xRatio = read_8u(table.data + ratioRangeOffset + 1); + r.yStartRatio = read_8u(table.data + ratioRangeOffset + 2); + r.yEndRatio = read_8u(table.data + ratioRangeOffset + 3); + + uint16_t groupOffset = read_16u(table.data + offsetOffset); + uint16_t recs = read_16u(table.data + groupOffset + 0); + for (uint16_t j = 0; j < recs; j++) { + uint16_t yPelHeight = read_16u(table.data + groupOffset + 4 + j * 6 + 0); + int16_t yMax = read_16s(table.data + groupOffset + 4 + j * 6 + 2); + int16_t yMin = read_16s(table.data + groupOffset + 4 + j * 6 + 4); + vdmx_iGroup.push(&r.records, (vdmx_Record){ + .yPelHeight = yPelHeight, + .yMax = yMax, + .yMin = yMin, + }); + } + vdmx_iRatioRangeList.push(&vdmx->ratios, r); + } + return vdmx; + FAIL: + logWarning("Table 'VDMX' corrupted.\n"); + table_iVDMX.free(vdmx); + vdmx = NULL; + } + return vdmx; +} + +void otfcc_dumpVDMX(const table_VDMX *vdmx, json_value *root, const otfcc_Options *options) { + if (!vdmx) return; + loggedStep("VDMX") { + json_value *_vdmx = json_object_new(2); + json_object_push(_vdmx, "version", json_integer_new(vdmx->version)); + + json_value *_ratios = json_array_new(vdmx->ratios.length); + json_object_push(_vdmx, "ratios", _ratios); + foreach (vdmx_RatioRange *rr, vdmx->ratios) { + json_value *_rr = json_object_new(5); + json_array_push(_ratios, _rr); + json_object_push(_rr, "bCharset", json_integer_new(rr->bCharset)); + json_object_push(_rr, "xRatio", json_integer_new(rr->xRatio)); + json_object_push(_rr, "yStartRatio", json_integer_new(rr->yStartRatio)); + json_object_push(_rr, "yEndRatio", json_integer_new(rr->yEndRatio)); + + json_value *_records = json_array_new(rr->records.length); + json_object_push(_rr, "records", _records); + foreach (vdmx_Record *r, rr->records) { + json_value *_r = json_object_new(3); + json_array_push(_records, _r); + json_object_push(_r, "yPelHeight", json_integer_new(r->yPelHeight)); + json_object_push(_r, "yMax", json_integer_new(r->yMax)); + json_object_push(_r, "yMin", json_integer_new(r->yMin)); + } + } + json_object_push(root, "VDMX", _vdmx); + } +} + +table_VDMX *otfcc_parseVDMX(const json_value *root, const otfcc_Options *options) { + json_value *_vdmx = NULL; + if (!(_vdmx = json_obj_get_type(root, "VDMX", json_object))) return NULL; + table_VDMX *vdmx = table_iVDMX.create(); + loggedStep("VDMX") { + vdmx->version = json_obj_getnum(_vdmx, "version"); + json_value *_ratios = json_obj_get_type(_vdmx, "ratios", json_array); + for (size_t j = 0; j < _ratios->u.array.length; j++) { + json_value *_rr = _ratios->u.array.values[j]; + if (!_rr || _rr->type != json_object) continue; + vdmx_RatioRange r; + vdmx_iRatioRange.init(&r); + r.bCharset = json_obj_getnum(_rr, "bCharset"); + r.xRatio = json_obj_getnum(_rr, "xRatio"); + r.yStartRatio = json_obj_getnum(_rr, "yStartRatio"); + r.yEndRatio = json_obj_getnum(_rr, "yEndRatio"); + json_value *_records = json_obj_get_type(_rr, "records", json_array); + if (!_records) { + vdmx_iRatioRange.dispose(&r); + continue; + } + for (size_t j = 0; j < _records->u.array.length; j++) { + json_value *_r = _records->u.array.values[j]; + if (!_r || _r->type != json_object) continue; + vdmx_iGroup.push(&r.records, (vdmx_Record){ + .yPelHeight = json_obj_getnum(_r, "yPelHeight"), + .yMax = json_obj_getnum(_r, "yMax"), + .yMin = json_obj_getnum(_r, "yMin"), + }); + } + vdmx_iRatioRangeList.push(&vdmx->ratios, r); + } + } + return vdmx; +} + +caryll_Buffer *otfcc_buildVDMX(const table_VDMX *vdmx, const otfcc_Options *options) { + if (!vdmx || !vdmx->ratios.length) return bufnew(); + bk_Block *root = bk_new_Block(b16, vdmx->version, // Version + b16, vdmx->ratios.length, // numRecs + b16, vdmx->ratios.length, // numRatios + bkover); + foreach (vdmx_RatioRange *rr, vdmx->ratios) { + bk_push(root, // RatioRange + b8, rr->bCharset, // bCharset + b8, rr->xRatio, // bCharset + b8, rr->yStartRatio, // bCharset + b8, rr->yEndRatio, // bCharset + bkover); + } + foreach (vdmx_RatioRange *rr, vdmx->ratios) { + uint16_t startsz = 0xFFFF; + uint16_t endsz = 0; + foreach (vdmx_Record *r, rr->records) { + if (startsz > r->yPelHeight) startsz = r->yPelHeight; + if (endsz < r->yPelHeight) endsz = r->yPelHeight; + } + bk_Block *group = bk_new_Block(b16, rr->records.length, // recs + b8, startsz, // + b8, endsz, // + bkover); + foreach (vdmx_Record *r, rr->records) { + bk_push(group, // + b16, r->yPelHeight, // + b16, r->yMax, // + b16, r->yMin, // + bkover); + } + bk_push(root, p16, group, bkover); + } + return bk_build_Block_noMinimize(root); +} diff --git a/lib/table/vdmx/type.c b/lib/table/vdmx/type.c new file mode 100644 index 00000000..3607de42 --- /dev/null +++ b/lib/table/vdmx/type.c @@ -0,0 +1,28 @@ +#include "../VDMX.h" + +#include "support/util.h" + +caryll_standardValType(vdmx_Record, vdmx_iRecord); + +caryll_standardVectorImpl(vdmx_Group, vdmx_Record, vdmx_iRecord, vdmx_iGroup); + +static void initRR(vdmx_RatioRange *rr) { + memset(rr, 0, sizeof(*rr)); + vdmx_iGroup.init(&rr->records); +} + +static void disposeRR(vdmx_RatioRange *rr) { + vdmx_iGroup.dispose(&rr->records); +} + +caryll_standardType(vdmx_RatioRange, vdmx_iRatioRange, initRR, disposeRR); +caryll_standardVectorImpl(vdmx_RatioRagneList, vdmx_RatioRange, vdmx_iRatioRange, + vdmx_iRatioRangeList); + +static void initVDMX(table_VDMX *t) { + vdmx_iRatioRangeList.init(&t->ratios); +} +static void disposeVDMX(table_VDMX *t) { + vdmx_iRatioRangeList.dispose(&t->ratios); +} +caryll_standardRefType(table_VDMX, table_iVDMX, initVDMX, disposeVDMX); diff --git a/lib/vf/axis.c b/lib/vf/axis.c new file mode 100644 index 00000000..60d76e21 --- /dev/null +++ b/lib/vf/axis.c @@ -0,0 +1,5 @@ +#include "otfcc/vf/axis.h" +#include "support/util.h" + +caryll_standardValType(vf_Axis, vf_iAxis); +caryll_standardVectorImpl(vf_Axes, vf_Axis, vf_iAxis, vf_iAxes); diff --git a/lib/vf/region.c b/lib/vf/region.c new file mode 100644 index 00000000..250bd708 --- /dev/null +++ b/lib/vf/region.c @@ -0,0 +1,62 @@ +#include "otfcc/vf/axis.h" +#include "support/util.h" + +vq_Region *vq_createRegion(shapeid_t dimensions) { + vq_Region *r; + NEW_CLEAN_S(r, (VQ_REGION_SIZE(dimensions))); + r->dimensions = dimensions; + return r; +} + +void vq_deleteRegion(MOVE vq_Region *region) { + FREE(region); +} + +vq_Region *vq_copyRegion(const vq_Region *region) { + vq_Region *dst = vq_createRegion(region->dimensions); + memcpy(dst, region, VQ_REGION_SIZE(region->dimensions)); + return dst; +} + +int vq_compareRegion(const vq_Region *a, const vq_Region *b) { + if (a->dimensions < b->dimensions) return -1; + if (a->dimensions > b->dimensions) return 1; + return strncmp((const char *)a, (const char *)b, VQ_REGION_SIZE(a->dimensions)); +} + +bool vq_AxisSpanIsOne(const vq_AxisSpan *s) { + const pos_t a = s->start; + const pos_t p = s->peak; + const pos_t z = s->end; + return a > p || p > z || (a < 0 && z > 0 && p != 0) || (p == 0); +} + +static pos_t INLINE weightAxisRegion(const vq_AxisSpan *as, const pos_t x) { + const pos_t a = as->start; + const pos_t p = as->peak; + const pos_t z = as->end; + if (a > p || p > z) { + return 1; + } else if (a < 0 && z > 0 && p != 0) { + return 1; + } else if (p == 0) { + return 1; + } else if (x < a || x > z) { + return 0; + } else if (x == p) { + return 1; + } else if (x < p) { + return (x - a) / (p - a); + } else { + return (z - x) / (z - p); + } +} +pos_t vqRegionGetWeight(const vq_Region *r, const VV *v) { + pos_t w = 1; + for (size_t j = 0; j < r->dimensions && v->length; j++) { + w *= weightAxisRegion(&r->spans[j], v->items[j]); + } + return w; +} + +void vq_showRegion(const vq_Region *r) {} diff --git a/lib/vf/vq.c b/lib/vf/vq.c index 417dfee0..4fc8a861 100644 --- a/lib/vf/vq.c +++ b/lib/vf/vq.c @@ -14,72 +14,8 @@ static VV createNeutralVV(tableid_t dimensions) { return vv; }; caryll_VectorInterfaceTypeName(VV) iVV = { - caryll_VectorImplAssignments(VV, pos_t, vq_iPosT), .neutral = createNeutralVV, -}; - -// Axis span and region -static INLINE void initAxisSpan(vq_AxisSpan *as) { - as->start = 0; - as->peak = 0; - as->end = 0; -} -static INLINE void copyAxisSpan(vq_AxisSpan *dst, const vq_AxisSpan *src) { - dst->start = src->start; - dst->peak = src->peak; - dst->end = src->end; -} -static INLINE void disposeAxisSpan(vq_AxisSpan *as) {} -caryll_standardType(vq_AxisSpan, vq_iAxisSpan, initAxisSpan, copyAxisSpan, disposeAxisSpan); - -static pos_t INLINE weightAxisRegion(const vq_AxisSpan *as, const pos_t x) { - const pos_t a = as->start; - const pos_t p = as->peak; - const pos_t z = as->end; - if (a > p || p > z) { - return 1; - } else if (a < 0 && z > 0 && p != 0) { - return 1; - } else if (p == 0) { - return 1; - } else if (x < a || x > z) { - return 0; - } else if (x == p) { - return 1; - } else if (x < p) { - return (x - a) / (p - a); - } else { - return (z - x) / (z - p); - } -} -static pos_t vqRegionGetWeight(const vq_Region *r, const VV *v) { - pos_t w = 1; - for (size_t j = 0; j < r->length && v->length; j++) { - w *= weightAxisRegion(&r->items[j], v->items[j]); - } - return w; -} - -static int vqrCompare(const vq_Region a, const vq_Region b) { - if (a.length < b.length) return -1; - if (a.length > b.length) return 1; - for (size_t j = 0; j < a.length; j++) { - if (a.items[j].start < b.items[j].start) return -1; - if (a.items[j].start > b.items[j].start) return 1; - if (a.items[j].peak < b.items[j].peak) return -1; - if (a.items[j].peak > b.items[j].peak) return 1; - if (a.items[j].end < b.items[j].end) return -1; - if (a.items[j].end > b.items[j].end) return 1; - } - return 0; -} - -caryll_VectorImplFunctions(vq_Region, vq_AxisSpan, vq_iAxisSpan); -caryll_OrdEqFns(vq_Region, vqrCompare); - -caryll_VectorInterfaceTypeName(vq_Region) vq_iRegion = { - caryll_VectorImplAssignments(vq_Region, vq_AxisSpan, vq_iAxisSpan), - caryll_OrdEqAssigns(vq_Region), // Ord - .getWeight = vqRegionGetWeight, + caryll_VectorImplAssignments(VV, pos_t, vq_iPosT), + .neutral = createNeutralVV, }; // VQS @@ -95,13 +31,12 @@ static INLINE void copyVQSegment(vq_Segment *dst, const vq_Segment *src) { break; case VQ_DELTA: dst->val.delta.quantity = src->val.delta.quantity; - vq_iRegion.copy(&dst->val.delta.region, &src->val.delta.region); + dst->val.delta.region = src->val.delta.region; } } static INLINE void disposeVQSegment(vq_Segment *vqs) { switch (vqs->type) { case VQ_DELTA: - vq_iRegion.dispose(&vqs->val.delta.region); break; default:; } @@ -115,7 +50,7 @@ static vq_Segment vqsCreateStill(pos_t x) { vqs.val.still = x; return vqs; } -static vq_Segment vqsCreateDelta(pos_t delta, MOVE vq_Region region) { +static vq_Segment vqsCreateDelta(pos_t delta, vq_Region *region) { vq_Segment vqs; vq_iSegment.init(&vqs); vqs.type = VQ_DELTA; @@ -134,7 +69,7 @@ static int vqsCompare(const vq_Segment a, const vq_Segment b) { return 0; } case VQ_DELTA: { - int vqrc = vqrCompare(a.val.delta.region, b.val.delta.region); + int vqrc = vq_compareRegion(a.val.delta.region, b.val.delta.region); if (vqrc) return vqrc; if (a.val.delta.quantity < b.val.delta.quantity) return -1; if (a.val.delta.quantity > b.val.delta.quantity) return 1; @@ -148,6 +83,12 @@ static void showVQS(const vq_Segment x) { case VQ_STILL: fprintf(stderr, "%g", x.val.still); return; + case VQ_DELTA: + fprintf(stderr, "{%g%s", x.val.delta.quantity, x.val.delta.touched ? " " : "* "); + vq_showRegion(x.val.delta.region); + fprintf(stderr, "}\n"); + return; + default:; } } @@ -157,7 +98,8 @@ caryll_ElementInterfaceOf(vq_Segment) vq_iSegment = { caryll_OrdEqAssigns(vq_Segment), // Ord and Eq caryll_ShowAssigns(vq_Segment), // Show // creating instances - .createStill = vqsCreateStill, .createDelta = vqsCreateDelta, + .createStill = vqsCreateStill, + .createDelta = vqsCreateDelta, }; caryll_standardVectorImpl(vq_SegList, vq_Segment, vq_iSegment, vq_iSegList); @@ -187,7 +129,7 @@ static bool vqsCompatible(const vq_Segment a, const vq_Segment b) { case VQ_STILL: return true; case VQ_DELTA: - return vq_iRegion.equal(a.val.delta.region, b.val.delta.region); + return 0 == vq_compareRegion(a.val.delta.region, b.val.delta.region); } } static void simplifyVq(MODIFY VQ *x) { diff --git a/premake5.lua b/premake5.lua index 93733245..61400a92 100644 --- a/premake5.lua +++ b/premake5.lua @@ -2,8 +2,8 @@ require "dep/premake-modules/xcode-alt" require "dep/premake-modules/ninja" MAIN_VER = '0' -SECONDARY_VER = '9' -PATCH_VER = '6' +SECONDARY_VER = '10' +PATCH_VER = '0' function cbuildoptions() -- Windows diff --git a/src/otfccdump.c b/src/otfccdump.c index 7051da89..30910007 100644 --- a/src/otfccdump.c +++ b/src/otfccdump.c @@ -38,6 +38,7 @@ void printHelp() { " --decimal-cmap : Export 'cmap' keys as decimal number.\n" " --hex-cmap : Export 'cmap' keys as hex number (U+FFFF).\n" " --name-by-hash : Name glyphs using its hash value.\n" + " --name-by-gid : Name glyphs using its glyph id.\n" " --add-bom : Add BOM mark in the output. (It is default on Windows\n" " when redirecting to another program. Use --no-bom to\n" " turn it off.)\n" @@ -70,6 +71,7 @@ int main(int argc, char *argv[]) { {"decimal-cmap", no_argument, NULL, 0}, {"instr-as-bytes", no_argument, NULL, 0}, {"name-by-hash", no_argument, NULL, 0}, + {"name-by-gid", no_argument, NULL, 0}, {"glyph-name-prefix", required_argument, NULL, 0}, {"verbose", no_argument, NULL, 0}, {"quiet", no_argument, NULL, 0}, @@ -117,6 +119,8 @@ int main(int argc, char *argv[]) { options->decimal_cmap = false; } else if (strcmp(longopts[option_index].name, "name-by-hash") == 0) { options->name_glyphs_by_hash = true; + } else if (strcmp(longopts[option_index].name, "name-by-gid") == 0) { + options->name_glyphs_by_gid = true; } else if (strcmp(longopts[option_index].name, "instr-as-bytes") == 0) { options->instr_as_bytes = true; } else if (strcmp(longopts[option_index].name, "glyph-name-prefix") == 0) { diff --git a/tests/payload/WorkSans-Regular.json b/tests/payload/WorkSans-Regular.json index 3a78d381..2d864baa 100644 --- a/tests/payload/WorkSans-Regular.json +++ b/tests/payload/WorkSans-Regular.json @@ -24,7 +24,7 @@ "ascender": 930, "descender": -243, "lineGap": 0, - "advanceWithMax": 1258, + "advanceWidthMax": 1258, "minLeftSideBearing": -199, "minRightSideBearing": -207, "xMaxExtent": 1204, @@ -134,7 +134,7 @@ "encodingID": 0, "languageID": 0, "nameID": 0, - "nameString": "Copyright © 2015 by Wei Huang. All rights reserved." + "nameString": "Copyright � 2015 by Wei Huang. All rights reserved." }, { "platformID": 1, diff --git a/tests/payload/iosevka-r.json b/tests/payload/iosevka-r.json index 721d4bb4..f511609b 100644 --- a/tests/payload/iosevka-r.json +++ b/tests/payload/iosevka-r.json @@ -25,7 +25,7 @@ "ascender": 977, "descender": -205, "lineGap": 67, - "advanceWithMax": 1000, + "advanceWidthMax": 1000, "minLeftSideBearing": -887, "minRightSideBearing": -887, "xMaxExtent": 1387, diff --git a/tests/payload/kltf-bugfont1.json b/tests/payload/kltf-bugfont1.json index 7138fc5c..abfa96ca 100644 --- a/tests/payload/kltf-bugfont1.json +++ b/tests/payload/kltf-bugfont1.json @@ -24,7 +24,7 @@ "ascender": 850, "descender": -350, "lineGap": 0, - "advanceWithMax": 713, + "advanceWidthMax": 713, "minLeftSideBearing": 25, "minRightSideBearing": 25, "xMaxExtent": 643,