~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/wineps.drv/type42.c

Version: ~ [ wine-1.5.30 ] ~ [ wine-1.5.29 ] ~ [ wine-1.5.28 ] ~ [ wine-1.5.27 ] ~ [ wine-1.5.26 ] ~ [ wine-1.5.25 ] ~ [ wine-1.5.24 ] ~ [ wine-1.5.23 ] ~ [ wine-1.5.22 ] ~ [ wine-1.5.21 ] ~ [ wine-1.5.20 ] ~ [ wine-1.5.19 ] ~ [ wine-1.5.18 ] ~ [ wine-1.5.17 ] ~ [ wine-1.5.16 ] ~ [ wine-1.5.15 ] ~ [ wine-1.5.14 ] ~ [ wine-1.5.13 ] ~ [ wine-1.5.12 ] ~ [ wine-1.5.11 ] ~ [ wine-1.5.10 ] ~ [ wine-1.5.9 ] ~ [ wine-1.5.8 ] ~ [ wine-1.5.7 ] ~ [ wine-1.4.1 ] ~ [ wine-1.5.6 ] ~ [ wine-1.5.5 ] ~ [ wine-1.5.4 ] ~ [ wine-1.5.3 ] ~ [ wine-1.5.2 ] ~ [ wine-1.5.1 ] ~ [ wine-1.5.0 ] ~ [ wine-1.4 ] ~ [ wine-1.4-rc6 ] ~ [ wine-1.4-rc5 ] ~ [ wine-1.4-rc4 ] ~ [ wine-1.4-rc3 ] ~ [ wine-1.4-rc2 ] ~ [ wine-1.4-rc1 ] ~ [ wine-1.3.37 ] ~ [ wine-1.3.36 ] ~ [ wine-1.3.35 ] ~ [ wine-1.3.34 ] ~ [ wine-1.3.33 ] ~ [ wine-1.3.32 ] ~ [ wine-1.3.31 ] ~ [ wine-1.3.30 ] ~ [ wine-1.3.29 ] ~ [ wine-1.3.28 ] ~ [ wine-1.3.27 ] ~ [ wine-1.3.26 ] ~ [ wine-1.3.25 ] ~ [ wine-1.3.24 ] ~ [ wine-1.3.23 ] ~ [ wine-1.3.22 ] ~ [ wine-1.3.21 ] ~ [ wine-1.3.20 ] ~ [ wine-1.3.19 ] ~ [ wine-1.3.18 ] ~ [ wine-1.2.3 ] ~ [ wine-1.3.17 ] ~ [ wine-1.3.16 ] ~ [ wine-1.3.15 ] ~ [ wine-1.3.14 ] ~ [ wine-1.3.13 ] ~ [ wine-1.3.12 ] ~ [ wine-1.3.11 ] ~ [ wine-1.3.10 ] ~ [ wine-1.3.9 ] ~ [ wine-1.2.2 ] ~ [ wine-1.3.8 ] ~ [ wine-1.3.7 ] ~ [ wine-1.3.6 ] ~ [ wine-1.3.5 ] ~ [ wine-1.2.1 ] ~ [ wine-1.3.4 ] ~ [ wine-1.3.3 ] ~ [ wine-1.3.2 ] ~ [ wine-1.3.1 ] ~ [ wine-1.3.0 ] ~ [ wine-1.2 ] ~ [ wine-1.2-rc7 ] ~ [ wine-1.2-rc6 ] ~ [ wine-1.2-rc5 ] ~ [ wine-1.2-rc4 ] ~ [ wine-1.2-rc3 ] ~ [ wine-1.2-rc2 ] ~ [ wine-1.2-rc1 ] ~ [ wine-1.1.44 ] ~ [ wine-1.1.43 ] ~ [ wine-1.1.42 ] ~ [ wine-1.1.41 ] ~ [ wine-1.1.40 ] ~ [ wine-1.1.39 ] ~ [ wine-1.1.38 ] ~ [ wine-1.1.37 ] ~ [ wine-1.1.36 ] ~ [ wine-1.1.35 ] ~ [ wine-1.1.34 ] ~ [ wine-1.1.33 ] ~ [ wine-1.1.32 ] ~ [ wine-1.1.31 ] ~ [ wine-1.1.30 ] ~ [ wine-1.1.29 ] ~ [ wine-1.1.28 ] ~ [ wine-1.1.27 ] ~ [ wine-1.1.26 ] ~ [ wine-1.1.25 ] ~ [ wine-1.1.24 ] ~ [ wine-1.1.23 ] ~ [ wine-1.1.22 ] ~ [ wine-1.1.21 ] ~ [ wine-1.1.20 ] ~ [ wine-1.1.19 ] ~ [ wine-1.1.18 ] ~ [ wine-1.1.17 ] ~ [ wine-1.1.16 ] ~ [ wine-1.1.15 ] ~ [ wine-1.1.14 ] ~ [ wine-1.1.13 ] ~ [ wine-1.1.12 ] ~ [ wine-1.1.11 ] ~ [ wine-1.1.10 ] ~ [ wine-1.1.9 ] ~ [ wine-1.1.8 ] ~ [ wine-1.1.7 ] ~ [ wine-1.0.1 ] ~ [ wine-1.1.6 ] ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~

  1 /*
  2  *      PostScript driver Type42 font functions
  3  *
  4  *      Copyright 2002  Huw D M Davies for CodeWeavers
  5  *
  6  * This library is free software; you can redistribute it and/or
  7  * modify it under the terms of the GNU Lesser General Public
  8  * License as published by the Free Software Foundation; either
  9  * version 2.1 of the License, or (at your option) any later version.
 10  *
 11  * This library is distributed in the hope that it will be useful,
 12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 14  * Lesser General Public License for more details.
 15  *
 16  * You should have received a copy of the GNU Lesser General Public
 17  * License along with this library; if not, write to the Free Software
 18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 19  */
 20 
 21 #include "config.h"
 22 #include "wine/port.h"
 23 
 24 #include <string.h>
 25 #include <stdlib.h>
 26 #include <stdarg.h>
 27 #include <stdio.h>
 28 #include <assert.h>
 29 #include <locale.h>
 30 
 31 #include "windef.h"
 32 #include "winbase.h"
 33 #include "wingdi.h"
 34 
 35 #include "psdrv.h"
 36 #include "wine/debug.h"
 37 
 38 WINE_DEFAULT_DEBUG_CHANNEL(psdrv);
 39 
 40 
 41 #define GET_BE_WORD(ptr)  MAKEWORD( ((BYTE *)(ptr))[1], ((BYTE *)(ptr))[0] )
 42 #define GET_BE_DWORD(ptr) ((DWORD)MAKELONG( GET_BE_WORD(&((WORD *)(ptr))[1]), \
 43                                             GET_BE_WORD(&((WORD *)(ptr))[0]) ))
 44 
 45 #define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
 46           ( ( (DWORD)_x4 << 24 ) |     \
 47             ( (DWORD)_x3 << 16 ) |     \
 48             ( (DWORD)_x2 <<  8 ) |     \
 49               (DWORD)_x1         )
 50 
 51 typedef struct {
 52   DWORD MS_tag;
 53   DWORD len, check;
 54   BYTE *data;
 55   BOOL write;
 56 } OTTable;
 57 
 58 static const OTTable tables_templ[] = {
 59       { MS_MAKE_TAG('c','v','t',' '), 0, 0, NULL, TRUE },
 60       { MS_MAKE_TAG('f','p','g','m'), 0, 0, NULL, TRUE },
 61       { MS_MAKE_TAG('g','d','i','r'), 0, 0, NULL, TRUE },
 62       { MS_MAKE_TAG('g','l','y','f'), 0, 0, NULL, FALSE },
 63       { MS_MAKE_TAG('h','e','a','d'), 0, 0, NULL, TRUE },
 64       { MS_MAKE_TAG('h','h','e','a'), 0, 0, NULL, TRUE },
 65       { MS_MAKE_TAG('h','m','t','x'), 0, 0, NULL, TRUE },
 66       { MS_MAKE_TAG('l','o','c','a'), 0, 0, NULL, TRUE },
 67       { MS_MAKE_TAG('m','a','x','p'), 0, 0, NULL, TRUE },
 68       { MS_MAKE_TAG('p','r','e','p'), 0, 0, NULL, TRUE },
 69       { 0, 0, 0, NULL, 0 }
 70 };
 71 
 72 struct tagTYPE42 {
 73     OTTable tables[sizeof(tables_templ)/sizeof(tables_templ[0])];
 74     int glyf_tab, loca_tab, head_tab; /* indices of glyf, loca and head tables */
 75     int hmtx_tab, maxp_tab;
 76     int num_of_written_tables;
 77     DWORD glyph_sent_size;
 78     BOOL *glyph_sent;
 79     DWORD emsize;
 80     DWORD *glyf_blocks;
 81 };
 82 
 83 #define GLYPH_SENT_INC 128
 84 
 85 #define FLIP_ORDER(x) \
 86  ( ( ((x) & 0xff) << 24) | \
 87    ( ((x) & 0xff00) << 8) | \
 88    ( ((x) & 0xff0000) >> 8) | \
 89    ( ((x) & 0xff000000) >> 24) )
 90 
 91 
 92 /* Some flags for composite glyphs.  See glyf table in OT spec */
 93 #define ARG_1_AND_2_ARE_WORDS    (1L << 0)
 94 #define WE_HAVE_A_SCALE          (1L << 3)
 95 #define MORE_COMPONENTS          (1L << 5)
 96 #define WE_HAVE_AN_X_AND_Y_SCALE (1L << 6)
 97 #define WE_HAVE_A_TWO_BY_TWO     (1L << 7)
 98 
 99 
100 static BOOL LoadTable(HDC hdc, OTTable *table)
101 {
102     unsigned int i;
103 
104     if(table->MS_tag == MS_MAKE_TAG('g','d','i','r')) return TRUE;
105     table->len = GetFontData(hdc, table->MS_tag, 0, NULL, 0);
106     table->data = HeapAlloc(GetProcessHeap(), 0, (table->len + 3) & ~3 );
107     memset(table->data + ((table->len - 1) & ~3), 0, sizeof(DWORD));
108     GetFontData(hdc, table->MS_tag, 0, table->data, table->len);
109     table->check = 0;
110     for(i = 0; i < (table->len + 3) / 4; i++)
111         table->check += FLIP_ORDER(*((DWORD*)(table->data) + i));
112     return TRUE;
113 }
114 
115 static BOOL get_glyf_pos(TYPE42 *t42, DWORD index, DWORD *start, DWORD *end)
116 {
117     WORD loca_format = GET_BE_WORD(t42->tables[t42->head_tab].data + 50);
118     TRACE("loca_format = %d\n", loca_format);
119     switch(loca_format) {
120     case 0:
121         *start = GET_BE_WORD(((WORD*)t42->tables[t42->loca_tab].data) + index);
122         *start <<= 1;
123         *end = GET_BE_WORD(((WORD*)t42->tables[t42->loca_tab].data) + index + 1);
124         *end <<= 1;
125         break;
126     case 1:
127         *start = GET_BE_DWORD(((DWORD*)t42->tables[t42->loca_tab].data) + index);
128         *end = GET_BE_DWORD(((DWORD*)t42->tables[t42->loca_tab].data) + index + 1);
129         break;
130     default:
131         ERR("Unknown loca_format %d\n", loca_format);
132         return FALSE;
133     }
134     return TRUE;
135 }
136 
137 TYPE42 *T42_download_header(PSDRV_PDEVICE *physDev, char *ps_name,
138                             RECT *bbox, UINT emsize)
139 {
140     DWORD i, j, tablepos, nb_blocks, glyf_off = 0, loca_off = 0, cur_off;
141     WORD num_of_tables = sizeof(tables_templ) / sizeof(tables_templ[0]) - 1;
142     char *buf;
143     TYPE42 *t42;
144     static const char start[] = /* name, fontbbox */
145             "25 dict begin\n"
146             " /FontName /%s def\n"
147             " /Encoding 256 array 0 1 255{1 index exch /.notdef put} for\n"
148             " def\n"
149             " /PaintType 0 def\n"
150             " /FontMatrix [1 0 0 1 0 0] def\n"
151             " /FontBBox [%f %f %f %f] def\n"
152             " /FontType 42 def\n"
153             " /CharStrings 256 dict begin\n"
154             "  /.notdef 0 def\n"
155             " currentdict end def\n"
156             " /sfnts [\n";
157     static const char TT_offset_table[] = "<00010000%04x%04x%04x%04x\n";
158     static const char TT_table_dir_entry[] = "%08x%08x%08x%08x\n";
159     static const char storage[] ="]\nhavetype42gdir{pop}{{string} forall}ifelse\n";
160     static const char end[] = "] def\n"
161       "havetype42gdir{/GlyphDirectory 256 dict def\n"
162       " sfnts 0 get dup\n"
163       "  %d <6c6f6378000000000000000000000000> putinterval\n" /* replace loca entry with dummy locx */
164       "  %d <676c6678000000000000000000000000> putinterval\n" /* replace glyf entry with dummy glfx */
165       " }if\n"
166       "currentdict end dup /FontName get exch definefont pop\n";
167 
168 
169     t42 = HeapAlloc(GetProcessHeap(), 0, sizeof(*t42));
170     memcpy(t42->tables, tables_templ, sizeof(tables_templ));
171     t42->loca_tab = t42->glyf_tab = t42->head_tab = t42->hmtx_tab = -1;
172     t42->emsize = emsize;
173     t42->num_of_written_tables = 0;
174 
175     for(i = 0; i < num_of_tables; i++) {
176         LoadTable(physDev->hdc, t42->tables + i);
177         if(t42->tables[i].len > 0xffff && t42->tables[i].write) break;
178         if(t42->tables[i].write) t42->num_of_written_tables++;
179         if(t42->tables[i].MS_tag == MS_MAKE_TAG('l','o','c','a'))
180             t42->loca_tab = i;
181         else if(t42->tables[i].MS_tag == MS_MAKE_TAG('g','l','y','f'))
182             t42->glyf_tab = i;
183         else if(t42->tables[i].MS_tag == MS_MAKE_TAG('h','e','a','d'))
184             t42->head_tab = i;
185         else if(t42->tables[i].MS_tag == MS_MAKE_TAG('h','m','t','x'))
186             t42->hmtx_tab = i;
187         else if(t42->tables[i].MS_tag == MS_MAKE_TAG('m','a','x','p'))
188             t42->maxp_tab = i;
189     }
190     if(i < num_of_tables) {
191         TRACE("Table %d has length %d.  Will use Type 1 font instead.\n", i, t42->tables[i].len);
192         T42_free(t42);
193         return NULL;
194     }
195 
196     t42->glyph_sent_size = GLYPH_SENT_INC;
197     t42->glyph_sent = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
198                                 t42->glyph_sent_size *
199                                 sizeof(*(t42->glyph_sent)));
200 
201     buf = HeapAlloc(GetProcessHeap(), 0, sizeof(start) + strlen(ps_name) +
202                     100);
203 
204     push_lc_numeric("C");
205     sprintf(buf, start, ps_name,
206             (float)bbox->left / emsize, (float)bbox->bottom / emsize,
207             (float)bbox->right / emsize, (float)bbox->top / emsize);
208     pop_lc_numeric();
209 
210     PSDRV_WriteSpool(physDev, buf, strlen(buf));
211 
212     t42->num_of_written_tables++; /* explicitly add glyf */
213     sprintf(buf, TT_offset_table, t42->num_of_written_tables,
214             t42->num_of_written_tables, t42->num_of_written_tables, t42->num_of_written_tables);
215 
216     PSDRV_WriteSpool(physDev, buf, strlen(buf));
217 
218     tablepos = 12 + t42->num_of_written_tables * 16;
219     cur_off = 12;
220     for(i = 0; i < num_of_tables; i++) {
221         if(!t42->tables[i].write) continue;
222         sprintf(buf, TT_table_dir_entry, FLIP_ORDER(t42->tables[i].MS_tag),
223                 t42->tables[i].check, t42->tables[i].len ? tablepos : 0,
224                 t42->tables[i].len);
225         PSDRV_WriteSpool(physDev, buf, strlen(buf));
226         tablepos += ((t42->tables[i].len + 3) & ~3);
227         if(t42->tables[i].MS_tag == MS_MAKE_TAG('l','o','c','a'))
228             loca_off = cur_off;
229         cur_off += 16;
230     }
231     sprintf(buf, TT_table_dir_entry, FLIP_ORDER(t42->tables[t42->glyf_tab].MS_tag),
232             t42->tables[t42->glyf_tab].check, tablepos, t42->tables[t42->glyf_tab].len);
233     PSDRV_WriteSpool(physDev, buf, strlen(buf));
234     PSDRV_WriteSpool(physDev, "00>\n", 4); /* add an extra byte for old PostScript rips */
235     glyf_off = cur_off;
236 
237     for(i = 0; i < num_of_tables; i++) {
238         if(t42->tables[i].len == 0 || !t42->tables[i].write) continue;
239         PSDRV_WriteSpool(physDev, "<", 1);
240         for(j = 0; j < ((t42->tables[i].len + 3) & ~3); j++) {
241             sprintf(buf, "%02x", t42->tables[i].data[j]);
242             PSDRV_WriteSpool(physDev, buf, strlen(buf));
243             if(j % 16 == 15) PSDRV_WriteSpool(physDev, "\n", 1);
244         }
245         PSDRV_WriteSpool(physDev, "00>\n", 4); /* add an extra byte for old PostScript rips */
246     }
247     
248     /* glyf_blocks is a 0 terminated list, holding the start offset of each block.  For simplicity
249        glyf_blocks[0] is 0 */
250     nb_blocks = 2;
251     t42->glyf_blocks = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (nb_blocks + 1) * sizeof(DWORD));
252     for(i = 0; i < GET_BE_WORD(t42->tables[t42->maxp_tab].data + 4); i++) {
253         DWORD start, end, size;
254         get_glyf_pos(t42, i, &start, &end);
255         size = end - t42->glyf_blocks[nb_blocks-2];
256         if(size > 0x2000 && t42->glyf_blocks[nb_blocks-1] % 4 == 0) {
257             nb_blocks++;
258             t42->glyf_blocks = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
259                                            t42->glyf_blocks, (nb_blocks + 1) * sizeof(DWORD));
260         }
261         t42->glyf_blocks[nb_blocks-1] = end;
262     }
263 
264     PSDRV_WriteSpool(physDev, "[ ", 2);
265     for(i = 1; t42->glyf_blocks[i]; i++) {
266         sprintf(buf,"%d ", t42->glyf_blocks[i] - t42->glyf_blocks[i-1] + 1);
267         /* again add one byte for old PostScript rips */
268         PSDRV_WriteSpool(physDev, buf, strlen(buf));
269         if(i % 8 == 0)
270             PSDRV_WriteSpool(physDev, "\n", 1);
271     }
272     PSDRV_WriteSpool(physDev, storage, sizeof(storage) - 1);
273     sprintf(buf, end, loca_off, glyf_off);
274     PSDRV_WriteSpool(physDev, buf, strlen(buf));
275     HeapFree(GetProcessHeap(), 0, buf);
276     return t42;
277 }
278 
279 
280 
281 
282 BOOL T42_download_glyph(PSDRV_PDEVICE *physDev, DOWNLOAD *pdl, DWORD index,
283                         char *glyph_name)
284 {
285     DWORD start, end, i;
286     char *buf;
287     TYPE42 *t42;
288 
289     const char glyph_def[] = 
290       "/%s findfont exch 1 index\n"
291       "havetype42gdir\n"
292       "{/GlyphDirectory get begin %d exch def end}\n"
293       "{/sfnts get 4 index get 3 index 2 index putinterval pop}\n"
294       "ifelse\n"
295       "/CharStrings get\n"
296       "begin\n"
297       " /%s %d def\n"
298       "end\n"
299       "pop pop\n";
300 
301     TRACE("%d %s\n", index, glyph_name);
302     assert(pdl->type == Type42);
303     t42 = pdl->typeinfo.Type42;
304 
305     if(index < t42->glyph_sent_size) {
306         if(t42->glyph_sent[index])
307             return TRUE;
308     } else {
309         t42->glyph_sent_size = (index / GLYPH_SENT_INC + 1) * GLYPH_SENT_INC;
310         t42->glyph_sent = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
311                                       t42->glyph_sent,
312                                       t42->glyph_sent_size * sizeof(*(t42->glyph_sent)));
313     }
314 
315     if(!get_glyf_pos(t42, index, &start, &end)) return FALSE;
316     TRACE("start = %x end = %x\n", start, end);
317 
318     if(GET_BE_WORD(t42->tables[t42->glyf_tab].data + start) == 0xffff) {
319       /* Composite glyph */
320         BYTE *sg_start = t42->tables[t42->glyf_tab].data + start + 10;
321         DWORD sg_flags, sg_index;
322         char sg_name[MAX_G_NAME + 1];
323 
324         do {
325             sg_flags = GET_BE_WORD(sg_start);
326             sg_index = GET_BE_WORD(sg_start + 2);
327 
328             TRACE("Sending subglyph %04x for glyph %04x\n", sg_index, index);
329             get_glyph_name(physDev->hdc, sg_index, sg_name);
330             T42_download_glyph(physDev, pdl, sg_index, sg_name);
331             sg_start += 4;
332             if(sg_flags & ARG_1_AND_2_ARE_WORDS)
333                 sg_start += 4;
334             else
335                 sg_start += 2;
336             if(sg_flags & WE_HAVE_A_SCALE)
337                 sg_start += 2;
338             else if(sg_flags & WE_HAVE_AN_X_AND_Y_SCALE)
339                 sg_start += 4;
340             else if(sg_flags & WE_HAVE_A_TWO_BY_TWO)
341                 sg_start += 8;
342         } while(sg_flags & MORE_COMPONENTS);
343     }
344 
345     for(i = 1; t42->glyf_blocks[i]; i++)
346         if(start < t42->glyf_blocks[i]) break;
347 
348     buf = HeapAlloc(GetProcessHeap(), 0, sizeof(glyph_def) +
349                     strlen(pdl->ps_name) + 100);
350 
351     /* we don't have a string for the gdir and glyf tables, but we do have a 
352        string for the TT header.  So the offset we need is tables - 2 */
353     sprintf(buf, "%d %d\n", t42->num_of_written_tables - 2 + i, start - t42->glyf_blocks[i-1]);
354     PSDRV_WriteSpool(physDev, buf, strlen(buf));
355 
356     PSDRV_WriteSpool(physDev, "<", 1);
357     for(i = start; i < end; i++) {
358         sprintf(buf, "%02x", *(t42->tables[t42->glyf_tab].data + i));
359         PSDRV_WriteSpool(physDev, buf, strlen(buf));
360         if((i - start) % 16 == 15)
361             PSDRV_WriteSpool(physDev, "\n", 1);
362     }
363     PSDRV_WriteSpool(physDev, ">\n", 2);
364     sprintf(buf, glyph_def, pdl->ps_name, index, glyph_name, index);
365     PSDRV_WriteSpool(physDev, buf, strlen(buf));
366 
367     t42->glyph_sent[index] = TRUE;
368     HeapFree(GetProcessHeap(), 0, buf);
369     return TRUE;
370 }
371 
372 void T42_free(TYPE42 *t42)
373 {
374     OTTable *table;
375     for(table = t42->tables; table->MS_tag; table++)
376         HeapFree(GetProcessHeap(), 0, table->data);
377     HeapFree(GetProcessHeap(), 0, t42->glyph_sent);
378     HeapFree(GetProcessHeap(), 0, t42->glyf_blocks);
379     HeapFree(GetProcessHeap(), 0, t42);
380     return;
381 }
382 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.