1 /* Copyright (C) 2001-2019 Artifex Software, Inc.
2    All Rights Reserved.
3 
4    This software is provided AS-IS with no warranty, either express or
5    implied.
6 
7    This software is distributed under license and may not be copied,
8    modified or distributed except as expressly authorized under the terms
9    of the license contained in the file LICENSE in this distribution.
10 
11    Refer to licensing information at http://www.artifex.com or contact
12    Artifex Software, Inc.,  1305 Grant Avenue - Suite 200, Novato,
13    CA 94945, U.S.A., +1(415)492-9861, for further information.
14 */
15 
16 
17 /* Glyph data cache methods. */
18 
19 #include "gx.h"
20 #include "gserrors.h"
21 #include "memory_.h"
22 #include "gsstruct.h"
23 #include "gsgdata.h"
24 #include "gsgcache.h"
25 #include "gxfont.h"
26 #include "gxfont42.h"
27 
28 /*
29  * This implementation hardcodes the type 42 font type.
30  * We could generalize it, but since CIDFontType 0 uses
31  * a PS procedure for reading glyphs, it is hardly applicable.
32  *
33  * The caching is mostly useful for glyphs with multiple components,
34  * but CIDFontType 0 has 2 components max, which are relatively seldom.
35  * Also it is low useful for fonts, which fully loaded into RAM.
36  * FAPI does not need a caching, because renderer pludins access
37  * font data through a file handle by own means.
38  *
39  * Due to all above, currently the caching is applied
40  * only while emulating CIDFontType 2 with a True Type file.
41  */
42 
43 typedef struct gs_glyph_cache_elem_s gs_glyph_cache_elem;
44 struct gs_glyph_cache_elem_s {
45     gs_glyph_data_t gd;
46     uint glyph_index;
47     uint lock_count;
48     gs_glyph_cache_elem *next;
49 };
50 gs_private_st_composite(st_glyph_cache_elem, gs_glyph_cache_elem, "gs_glyph_cache_elem",
51     gs_glyph_cache_elem_enum_ptrs, gs_glyph_cache_elem_reloc_ptrs);
52 
53 static
ENUM_PTRS_WITH(gs_glyph_cache_elem_enum_ptrs,gs_glyph_cache_elem * e)54 ENUM_PTRS_WITH(gs_glyph_cache_elem_enum_ptrs, gs_glyph_cache_elem *e)
55 {
56     index --;
57     if (index < ST_GLYPH_DATA_NUM_PTRS)
58         return ENUM_USING(st_glyph_data, &e->gd, sizeof(e->gd), index);
59     return 0;
60 }
61 ENUM_PTR(0, gs_glyph_cache_elem, next);
62 ENUM_PTRS_END
RELOC_PTRS_WITH(gs_glyph_cache_elem_reloc_ptrs,gs_glyph_cache_elem * e)63 static RELOC_PTRS_WITH(gs_glyph_cache_elem_reloc_ptrs, gs_glyph_cache_elem *e)
64 {
65     RELOC_PTR(gs_glyph_cache_elem, next);
66     RELOC_USING(st_glyph_data, &e->gd, sizeof(e->gd));
67 } RELOC_PTRS_END
68 
69 struct gs_glyph_cache_s {
70     int total_size;
71     gs_glyph_cache_elem *list;
72     gs_memory_t *memory;
73     gs_font_type42 *pfont;
74     stream *s;
75     get_glyph_data_from_file read_data;
76 };
77 gs_private_st_ptrs4(st_glyph_cache, gs_glyph_cache, "gs_glyph_cache",
78     gs_glyph_cache_enum_ptrs, gs_glyph_cache_reloc_ptrs, list, memory, pfont, s);
79 
80 GS_NOTIFY_PROC(gs_glpyh_cache__release);
81 
82 gs_glyph_cache *
gs_glyph_cache__alloc(gs_font_type42 * pfont,stream * s,get_glyph_data_from_file read_data)83 gs_glyph_cache__alloc(gs_font_type42 *pfont, stream *s,
84                         get_glyph_data_from_file read_data)
85 {
86     gs_memory_t *mem = pfont->memory->stable_memory;
87     gs_glyph_cache *gdcache = (gs_glyph_cache *)gs_alloc_struct(mem,
88             gs_glyph_cache, &st_glyph_cache, "gs_glyph_cache");
89     if (gdcache == 0)
90         return 0;
91     gdcache->total_size = 0;
92     gdcache->list = NULL;
93     gdcache->pfont = pfont;
94     gdcache->s = s;
95     /*
96     * The cache elements need to be in stable memory so they don't
97     * get removed by 'restore' (elements can be created at a different
98     * save level than the current level)
99     */
100     gdcache->memory = mem;
101     gdcache->read_data = read_data;
102     gs_font_notify_register((gs_font *)pfont, gs_glyph_cache__release, (void *)gdcache);
103     return gdcache;
104 }
105 
106 int
gs_glyph_cache__release(void * data,void * event)107 gs_glyph_cache__release(void *data, void *event)
108 {
109     gs_glyph_cache *self = (gs_glyph_cache *)data;
110     gs_glyph_cache_elem *e = self->list;
111     gs_font_type42 *pfont = self->pfont;
112 
113     while (e != NULL) {
114         gs_glyph_cache_elem *next_e;
115 
116         next_e = e->next;
117         e->gd.procs->free(&e->gd, "gs_glyph_cache__release");
118         gs_free_object(self->memory, e, "gs_glyph_cache_elem__release");
119         e = next_e;
120     }
121     self->list = NULL;
122     gs_font_notify_unregister((gs_font *)pfont, gs_glyph_cache__release, (void *)self);
123     gs_free_object(self->memory, self, "gs_glyph_cache__release");
124     return 0;
125 }
126 
127 static gs_glyph_cache_elem **
gs_glyph_cache_elem__locate(gs_glyph_cache * self,uint glyph_index)128 gs_glyph_cache_elem__locate(gs_glyph_cache *self, uint glyph_index)
129 {   /* If not fond, returns an unlocked element. */
130     gs_glyph_cache_elem **e = &self->list, **p_unlocked = NULL;
131     int count = 0; /* debug purpose only */
132 
133     for (; *e != 0; e = &(*e)->next, count++) {
134         if ((*e)->glyph_index == glyph_index) {
135             return e;
136         }
137         if ((*e)->lock_count == 0)
138             p_unlocked = e;
139     }
140     return p_unlocked;
141 }
142 
143 static inline void
gs_glyph_cache_elem__move_to_head(gs_glyph_cache * self,gs_glyph_cache_elem ** pe)144 gs_glyph_cache_elem__move_to_head(gs_glyph_cache *self, gs_glyph_cache_elem **pe)
145 {   gs_glyph_cache_elem *e = *pe;
146 
147     *pe = e->next;
148     e->next = self->list;
149     self->list = e;
150 }
151 
152 /* Manage the glyph data using the font's allocator. */
153 static void
gs_glyph_cache_elem__free_data(gs_glyph_data_t * pgd,client_name_t cname)154 gs_glyph_cache_elem__free_data(gs_glyph_data_t *pgd, client_name_t cname)
155 {   gs_glyph_cache_elem *e = (gs_glyph_cache_elem *)pgd->proc_data;
156 
157     e->lock_count--;
158 }
159 static int
gs_glyph_cache_elem__substring(gs_glyph_data_t * pgd,uint offset,uint size)160 gs_glyph_cache_elem__substring(gs_glyph_data_t *pgd, uint offset, uint size)
161 {   gs_glyph_cache_elem *e = (gs_glyph_cache_elem *)pgd->proc_data;
162 
163     e->lock_count++;
164     return_error(gs_error_unregistered); /* Unsupported; should not happen. */
165 }
166 
167 static const gs_glyph_data_procs_t gs_glyph_cache_elem_procs = {
168     gs_glyph_cache_elem__free_data, gs_glyph_cache_elem__substring
169 };
170 
171 int
gs_get_glyph_data_cached(gs_font_type42 * pfont,uint glyph_index,gs_glyph_data_t * pgd)172 gs_get_glyph_data_cached(gs_font_type42 *pfont, uint glyph_index, gs_glyph_data_t *pgd)
173 {   gs_glyph_cache *gdcache = pfont->data.gdcache;
174     gs_glyph_cache_elem **pe = gs_glyph_cache_elem__locate(gdcache, glyph_index);
175     gs_glyph_cache_elem *e = NULL;
176 
177     if (pe == NULL || (*pe)->glyph_index != glyph_index) {
178         int code;
179 
180         if (pe != NULL && gdcache->total_size > 32767 /* arbitrary */ &&
181                           (*pe)->lock_count <= 0) {
182             /* Release the element's data, and move it : */
183             e = *pe;
184             gdcache->total_size -= e->gd.bits.size + sizeof(*e);
185             e->gd.procs->free(&e->gd, "gs_get_glyph_data_cached");
186             gs_glyph_cache_elem__move_to_head(gdcache, pe);
187         } else {
188             /* Allocate new head element. */
189             e = (gs_glyph_cache_elem *)gs_alloc_struct(gdcache->memory,
190                 gs_glyph_cache_elem, &st_glyph_cache_elem, "gs_glyph_cache_elem");
191             if (e == NULL)
192                 return_error(gs_error_VMerror);
193             memset(e, 0, sizeof(*e));
194             e->next = gdcache->list;
195             gdcache->list = e;
196             e->gd.memory = gdcache->memory;
197         }
198         /* Load the element's data : */
199         code = (*gdcache->read_data)(pfont, gdcache->s, glyph_index, &e->gd);
200         if (code < 0)
201             return code;
202         gdcache->total_size += e->gd.bits.size + sizeof(*e);
203         e->glyph_index = glyph_index;
204     } else {
205         /* Move the element : */
206         e = *pe;
207         gs_glyph_cache_elem__move_to_head(gdcache, pe);
208     }
209     /* Copy data and set procs : */
210     pgd->bits = e->gd.bits;
211     pgd->proc_data = e;
212     pgd->procs = &gs_glyph_cache_elem_procs;
213     e->lock_count++;
214     return 0;
215 }
216