summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/GPDL.htm556
1 files changed, 556 insertions, 0 deletions
diff --git a/doc/GPDL.htm b/doc/GPDL.htm
new file mode 100644
index 000000000..c854fb417
--- /dev/null
+++ b/doc/GPDL.htm
@@ -0,0 +1,556 @@
1<!doctype html>
2<html>
3<head>
4<meta charset="UTF-8">
5<meta name="viewport" content="width=device-width, initial-scale=1.0">
6<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
7<link rel="shortcut icon" type="image/png" href="../../images/favicon.png">
8<title>The GhostPDL Interpreter Framework</title>
9<link href="style.css" rel="stylesheet" type="text/css">
10<link href="gs-style.css" rel="stylesheet" type="text/css">
11</head>
12
13<body>
14
15 <div class="header">
16 <div class="row">
17 <div class="col-lt-6 logo"><a href="https://www.ghostscript.com/"><img src="images/ghostscript_logo.png" width="108" height="119" alt=""></a></div>
18 <div class="col-6"><div class="row"><div class="artifexlogo"><a href="https://artifex.com" target="_blank"><img src="images/Artifex_logo.png" width="194" height="40" alt=""></a></div>
19 <div class="col-12"><div class="button button1"><a href="https://artifex.com/contact-us/" title="Contact Us" target="_blank">Contact Us</a></div>
20 <div class="button button2 hidden-xs"><a href="https://www.ghostscript.com/download.html" title="Download">Download</a></div></div></div>
21 </div>
22 </div>
23 </div>
24
25 <div class="banner">
26 <div class="row">
27 <div class="col-12">The GhostPDL Interpreter Framework</div>
28 </div>
29 </div>
30
31 <div class="main">
32 <div class="row">
33 <div id="sidebar">
34 <div class="sidebar-item"></div>
35 <div class="col-2 leftnav">
36<ul>
37 <li><a href="https://www.ghostscript.com/">Home</a></li>
38 <li><a href="https://www.ghostscript.com/license.html">Licensing</a></li>
39 <li><a href="https://www.ghostscript.com/releases.html">Releases</a></li>
40 <li><a href="https://www.ghostscript.com/release_history.html">Release History</a></li>
41 <li><a href="https://www.ghostscript.com/documentation.html" title="Documentation">Documentation</a></li>
42 <li><a href="https://www.ghostscript.com/download.html" title="Download">Download</a></li>
43 <li><a href="https://www.ghostscript.com/performance.html" title="Performance">Performance</a></li>
44 <li><a href="http://jbig2dec.com/" title="jbig2dec">jbig2dec</a></li>
45 <li><a href="http://git.ghostscript.com/?p=ghostpdl.git;a=summary">Source</a></li>
46 <li><a href="http://bugs.ghostscript.com/">Bugs</a></li>
47 <li><a href="https://www.ghostscript.com/faq.html" title="FAQ">FAQ</a></li>
48 </ul>
49 </div>
50 </div>
51 <div class="col-10 page">
52
53<!--START EDITING HERE-->
54
55<h2>Table of contents</h2>
56
57<ul>
58<li><a href="#What_Is_This">What is the GhostPDL Interpreter Framework?</a></li>
59<li><a href="#API">The API</a></li>
60<li><a href="#executable">The GPDL executable</a></li>
61<li><a href="#different_switches">Differences in switches from Ghostscript</a></li>
62<li><a href="#languages">Supported languages</a></li>
63 <ul>
64 <li><a href="#PJL">PJL</a></li>
65 <li><a href="#PCL">PCL</a></li>
66 <li><a href="#PCLXL">PCLXL</a></li>
67 <li><a href="#XPS">XPS</a></li>
68 <li><a href="#POSTSCRIPT">POSTSCRIPT</a></li>
69 <li><a href="#URF">URF</a></li>
70 <li><a href="#JPG">JPG</a></li>
71 <li><a href="#PWG">PWG</a></li>
72 <li><a href="#TIFF">TIFF</a></li>
73 <li><a href="#JBIG2">JBIG2</a></li>
74 <li><a href="#JP2K">JP2K</a></li>
75 <li><a href="#PNG">PNG</a></li>
76 </ul>
77<li><a href="#new_language">Adding a new language</a></li>
78 <ul>
79 <li><a href="proc_characteristics">proc_characteristics</a></li>
80 <li><a href="proc_allocate_interp_instance">proc_allocate_interp_instance</a></li>
81 <li><a href="proc_get_device_memory">proc_get_device_memory</a></li>
82 <li><a href="proc_set_param">proc_set_param</a></li>
83 <li><a href="proc_add_path">proc_add_path</a></li>
84 <li><a href="proc_post_args_init">proc_post_args_init</a></li>
85 <li><a href="proc_init_job">proc_init_job</a></li>
86 <li><a href="proc_run_prefix_commands">proc_run_prefix_commands</a></li>
87 <li><a href="proc_process_file">proc_process_file</a></li>
88 <li><a href="proc_process_begin">proc_process_begin</a></li>
89 <li><a href="proc_process">proc_process</a></li>
90 <li><a href="proc_process_end">proc_process_end</a></li>
91 <li><a href="proc_flush_to_eoj">proc_flush_to_eoj</a></li>
92 <li><a href="proc_process_eof">proc_process_eof</a></li>
93 <li><a href="proc_report_errors">proc_report_errors</a></li>
94 <li><a href="proc_dnit_job">proc_dnit_job</a></li>
95 <li><a href="proc_deallocate_interp_instance">proc_deallocate_interp_instance</a></li>
96 </ul>
97
98</ul>
99
100<!-- [1.2 end table of contents] =========================================== -->
101
102<!-- [1.3 begin hint] ====================================================== -->
103
104<p>For other information, see the <a href="Readme.htm">Ghostscript overview</a>.</p>
105
106<!-- [1.3 end hint] ======================================================== -->
107
108<hr>
109
110<!-- [1.0 end visible header] ============================================== -->
111
112<!-- [2.0 begin contents] ================================================== -->
113
114
115<h2><a name="What_Is_This"></a>What is the GhostPDL Interpreter Framework?</h2>
116
117<p>
118The GhostPDL interpreter framework (henceforth, just GhostPDL) is
119a framework into which multiple interpreters can be fitted, together
120with a set of interpreters for different input &quot;languages&quot;.</p>
121<p>The intent is that a build of GPDL will behave as much as possible
122like a build of Ghostscript, but should be able to transparently
123cope with as many different input formats as it has interpreters built
124into it.</p>
125<p>It can be built as a dynamic link library
126(DLL) on Microsoft Windows, as a shared object on the
127Linux, Unix and MacOS X platforms. With some changes, it could be built
128as a static library.</p>
129<p>The reason we dub it a &quot;Framework&quot; is that it is
130designed to be modular, and to readily allow new interpreters to be
131swapped in and out to extend and customise its capabilities.</p>
132
133<p>Jobs must be separated by <code>UEL</code> (Universal End of Language)
134code sequences (or PJL fragments, which implicitly start with a
135<code>UEL</code> marker).</p>
136
137<h2><a name="API"></a>The API</h2>
138
139<p>The API for GPDL is deliberately designed to be nearly identical
140to that for Ghostscript itself. Details of Ghostscripts API can be
141found <a href="API.htm">here</a>, and we refer you to that. Only
142differences from that API will be documented here. In particular
143the differences in the handling of switches within the
144<code>gsapi_init_with_args</code></p> are documented
145<a href="#different_switches">here</a>.
146<p>Our intent is that existing users of the Ghostscript API should be
147able to drop a GPDL DLL in as a replacement with little to no code
148changes.</p>
149
150<h2><a name="executable"></a>The GPDL executable</h2>
151
152<p>
153The GPDL executable is a small executable that loads the DLL/shared object,
154and implements, pretty much, the interface described in the
155<a href="Use.htm">usage documentation</a> for Ghostscript, but capable of
156reading any of the configured languages. This executable provides all
157the interaction with the windowing system, including image windows.</p>
158
159<p>Areas where the GPDL executable differs from the Ghostscript
160executable are described <a href="#different_switches">below</a>. These
161are primarily to do with (slightly) esoteric requirements when
162running Postscript in an interactive environment. Whereas Ghostscript
163is a generic Postscript interpreter, GPDL is unashamedly targeted
164as a print processor.</p>
165
166<p>
167The GPDL framework's library name and characteristics differ
168for each platform:</p>
169
170<ul>
171<li>The Win32 DLL <code>gpdldll32.dll</code>
172can be used by multiple programs simultaneously, but only once
173within each process.</li>
174
175<li>The Linux shared object <code>libgs.so</code>
176can be used by multiple programs simultaneously.</li>
177</ul>
178<p>
179The source for the executable is in <code>plw</code>*.* (Windows)
180and <code>plx</code>*.* (Linux/Unix).
181See these source files for examples of how to use the DLL.</p>
182
183<p>
184At this stage, GhostPDL does not support multiple instances
185of the interpreter within a single process.</p>
186
187<h2><a name="different_switches"></a>Differences in switches from Ghostscript</h3>
188
189<p>The <code>gsapi_init_with_args</code> API entrypoint takes a
190set of arguments, which can include various switches. We document
191the differences between Ghostscript's handling of switches and
192GhostPDLs here. The GhostPDL executable directly maps parameters
193given on the command-line into this list, so this list of differences
194applies both to API calls and uses of the executable.</p>
195
196<p>GhostPDL does not support the <code>-_</code>, <code>-+</code>,
197<code>-@</code>, <code>-B</code>, <code>-F</code>, <code>-M</code>,
198<code>-N</code>, <code>-p</code>, <code>-u</code>, and <code>-X</code>
199switches that Ghostscript does.
200
201<p>GhostPDL supports a few switches that the Ghostscript executable
202does not.</p>
203
204<ul>
205 <li><b><code>-E #</code>:</b> Sets the &quot;error reporting mode&quot;
206for PCL/PXL.</li>
207 <li><b><code>-H #x#x#x#</code>:</b> Sets the hardware margins to the
208given left/bottom/right/top values (in points).</li>
209 <li><b><code>-j &lt;string&gt;</code>:</b> Passes the string to the
210 PJL level. The string is split on ';'.</li>
211 <li><b><code>-J</code>:</b> Same as <code>-j</code>.</li>
212 <li><b><code>-l {RTL,PCL5E,PCL5C}</code>:</b> Sets the
213 &quot;personality&quot; of the PCL/PXL interpreter.</li>
214 <li><b><code>-L &lt;language&gt;</code>:</b> Sets the language
215 to be used. Run with <code>-L</code> and no string to see a list
216 of languages supported in your build.</li>
217 <li><b><code>-m #x#</code>:</b> Sets the margin values to the
218 left/bottom values (in points).</li>
219</ul>
220
221<h2><a name="languages"></a>Supported languages</h2>
222
223<p>The following is a (possibly non-exhaustive) list of languages
224for which implementations exist for GhostPDL. We use the term
225&quot;language&quot; to include both full page description languages
226(such as PCL, Postscript and XPS), and handlers for simpler formats
227(such as JPEG, PNG and TIFF).</p>
228<p>It is inherent in the design that new handlers can be included,
229and unwanted formats can be dropped from any given build/integration.</p>
230
231<h3><a name="PJL"></a>PJL</h3>
232<p>PJL is special, in that it is the central language implementation
233to which we return between all jobs. As such it always appears first
234in the list, and must be present in all builds.</p>
235<h3><a name="PCL"></a>PCL</h3>
236<p>The PCL language implementation supports PCL5C/PXL5E and HPGL/2
237with RTL.</p>
238<h3><a name="PCLXL"></a>PCLXL</h3>
239<p>The PCLXL language implementation supports PCLXL.</p>
240<h3><a name="XPS"></a>XPS</h3>
241<p>The XPS language implementation supports the XML Paper Specification
242format, as used in modern Windows systems printing pipelines.</p>
243<h3><a name="POSTSCRIPT"></a>POSTSCRIPT</h3>
244<p>The POSTSCRIPT language implementation is the Ghostscript
245 Postscript interpreter, as described in the accompanying
246 documentation.</p>
247<h3><a name="URF"></a>URF</h3>
248<p>Currently available to commercial customers only, the URF langauge
249implementation implements support for the URF file format, as used
250by Apple Airprint.</p>
251<h3><a name="JPG"></a>JPG</h3>
252<p>The JPG language implementation supports JPEG files
253(in both JFIF and EXIF formats).</p>
254<h3><a name="PWG"></a>PWG</h3>
255<p>The PWG language implementation supports PWG
256(Printer Working Group) format files.</p>
257<h3><a name="TIFF"></a>TIFF</h3>
258<p>The TIFF language implementation supports TIFF format files in
259a variety of compression formats, depending on the exact configuration
260of libtiff used in the build.</p>
261<h3><a name="JBIG2"></a>JBIG2</h3>
262<p>The JBIG2 language implementation supports JBIG2 format images.</p>
263<h3><a name="JP2K"></a>JP2K</h3>
264<p>The JP2K language implementation supports JPEG2000 and JPX format
265images.</p>
266<h3><a name="PNG"></a>PNG</h3>
267<p>The PNG language implementation supports PNG format images.</p>
268
269<h2><a name="new_language"></a>Adding a new language</h2>
270
271<p>Each language implementation in the system appears as an instance
272of a <code>pl_interp_implementation_t</code> structure.
273
274<pre>
275typedef struct pl_interp_implementation_s
276{
277 /* Procedure vector */
278 pl_interp_proc_characteristics_t proc_characteristics;
279 pl_interp_proc_allocate_interp_instance_t proc_allocate_interp_instance;
280 pl_interp_proc_get_device_memory_t proc_get_device_memory;
281 pl_interp_proc_set_param_t proc_set_param;
282 pl_interp_proc_add_path_t proc_add_path;
283 pl_interp_proc_post_args_init_t proc_post_args_init;
284 pl_interp_proc_init_job_t proc_init_job;
285 pl_interp_proc_run_prefix_commands_t proc_run_prefix_commands;
286 pl_interp_proc_process_file_t proc_process_file;
287 pl_interp_proc_process_begin_t proc_process_begin;
288 pl_interp_proc_process_t proc_process;
289 pl_interp_proc_process_end_t proc_process_end;
290 pl_interp_proc_flush_to_eoj_t proc_flush_to_eoj;
291 pl_interp_proc_process_eof_t proc_process_eof;
292 pl_interp_proc_report_errors_t proc_report_errors;
293 pl_interp_proc_dnit_job_t proc_dnit_job;
294 pl_interp_proc_deallocate_interp_instance_t
295 proc_deallocate_interp_instance;
296 void *interp_client_data;
297} pl_interp_implementation_t;
298</pre>
299
300<p>This structure consists of series of function pointers, each
301of which performs some portion of the processing required to handle
302detection of language type and processing of data. These function
303pointers are described in detail below.</p>
304<p>In addition, the <code>interp_client_data</code> field is
305used to hold the running state of a given interpreter.</p>
306
307<p>All the languages to be supported in the build are listed
308in the <code>pdl_implementations</code> array in
309<a href="../pcl/pl/plimpl.c">plimpl.c</a>. To add a new implementation
310the name of the appropriate <code>pl_interp_implementation_t</code>
311should be added here.</a>
312
313<p>The existing range of language implementations may prove useful
314as references when implementing new ones. They can be found as
315<code>gpdl/*top.c</code>. In particular,
316<code><a href="../gpdl/pngtop.c">pngtop.c</a></code> is a simple
317implementation of a well-defined, relatively simple file format
318(PNG), based upon a well-known and well-documented library (libpng),
319so is probably a good place to start.</p>
320
321<h3><a name="proc_characteristics"></a>proc_characteristics</h3>
322<pre>
323typedef const pl_interp_characteristics_t
324 *(*pl_interp_proc_characteristics_t) (const pl_interp_implementation_t *);
325</pre>
326<p>This entrypoint is called to request details of the characteristics
327of the language. This must be implemented in all instances.</p>
328<p>This returns a pointer to a <code>pl_interp_characteristics_t</code>
329 structure:</p>
330<pre>
331typedef struct pl_interp_characteristics_s
332{
333 const char *language; /* generic language should correspond with
334 HP documented PJL name */
335 int (*auto_sense)(const char *string, int length); /* routine used to detect language - returns a score: 0 is definitely not, 100 is definitely yes. */
336 const char *manufacturer; /* manuf str */
337 const char *version; /* version str */
338 const char *build_date; /* build date str */
339} pl_interp_characteristics_t;
340</pre>
341<p>The <code>language</code> entry contains a simple NULL terminated
342 string that names the interpreter. Similarly, the <code>manufacturer</code>,
343 <code>version</code>, and <code>build_date</code> fields are for
344 informational purposes only.</p>
345<p>The <code>auto_sense</code> function is called with a prefix of data
346 from each new source. Each language is passed the data in turn, and
347 &quot;scores;&quot; according to how sure it is the file is that format.</p>
348<p>For many file formats this means looking for known in the first
349few bytes (such as PNG or TIFF looking for their signature bytes). For
350others, such as XPS, the best that can be done is to spot that it's a
351 zip file. For still others, such as PCL, heuristics have to be used.</p>
352<p>A 'definite' match is returned as 100, a definite rejection as 0, with
353intermediate values used appropriately.</p>
354<h3><a name="proc_allocate_interp_instance"></a>proc_allocate_interp_instance</h3>
355<pre>
356typedef int (*pl_interp_proc_allocate_interp_instance_t)
357 (pl_interp_implementation_t *,
358 gs_memory_t *);
359</pre>
360<p>On startup, the GPDL library calls around all the languages
361via this function, getting them to allocate themselves an instance.
362 What this means will vary between languages, but typically it involves
363 allocating a state structure, and storing the pointer to that in the
364 <code>interp_client_data</code> pointer of the
365 <code>pl_interp_implementation_t *</code>. Part of this state structure
366 will typically be a <code>gstate</code> to use to drive the graphics
367 library.</p>
368<h3><a name="proc_get_device_memory"></a>proc_get_device_memory</h3>
369<pre>
370typedef gs_memory_t *(*pl_interp_proc_get_device_memory_t)
371 (pl_interp_implementation_t *);
372</pre>
373<p>On shutdown, the GPDL library calls around all the languages
374via this function, getting them to release their resources and
375deallocate any memory.</p>
376<h3><a name="proc_set_param"></a>proc_set_param</h3>
377<pre>
378typedef int (*pl_interp_proc_set_param_t) (pl_interp_implementation_t *,
379 pl_set_param_type,
380 const char *,
381 const void *);
382</pre>
383<p>Some languages (particularly Postscript) can have their behaviour
384changed by the use of parameters. This function provides a generic
385method for the GPDL library to pass parameters into a language. Each
386language is free to ignore the parameters that do not apply to it. For
387most languages, this can safely be passed as <code>NULL</code>.</p>
388<h3><a name="proc_add_path"></a>proc_add_path</h3>
389<pre>
390typedef int (*pl_interp_proc_add_path_t) (pl_interp_implementation_t *,
391 const char *);
392</pre>
393<p>Some languages (particularly Postscript) have the ability to
394 open files from the local storage. These files can be found
395 in a variety of different locations within the local storage.
396 As such this call allows the GPDL library to add paths to
397 the current list of locations that will be searched. For
398 most languages, this can safely be passed as <code>NULL</code>.</p>
399<h3><a name="proc_post_args_init"></a>proc_post_args_init</h3>
400<pre>
401typedef int (*pl_interp_proc_post_args_init_t) (pl_interp_implementation_t *);
402</pre>
403<p></p>
404<h3><a name="proc_init_job"></a>proc_init_job</h3>
405<pre>
406typedef int (*pl_interp_proc_init_job_t) (pl_interp_implementation_t *,
407 gx_device *);
408</pre>
409<p>Once the GPDL library has identified which language should be used
410for an incoming job, it will call this entrypoint to initialise the
411language for the job. What this means will vary between languages,
412but at the very minimum the job will need to take note of the device
413to be used.</p>
414<h3><a name="proc_run_prefix_commands"></a>proc_run_prefix_commands</h3>
415<pre>
416typedef int (*pl_interp_proc_run_prefix_commands_t)
417 (pl_interp_implementation_t *,
418 const char *prefix);
419</pre>
420<p>The GPDL library (and executable) allow language commands to be
421sent in the argument parameters using the <code>-c</code> switch. These
422are collected into a buffer, and forwarded to a language to be run
423as part of the same job as any following file.</p>
424<p>Currently, only the Postscript language handler implements this
425function, all others should pass <code>NULL</code>.</p>
426<h3><a name="proc_process_file"></a>proc_process_file</h3>
427<pre>
428typedef int (*pl_interp_proc_process_file_t) (pl_interp_implementation_t *,
429 const char *);
430</pre>
431<p>If the GPDL library is given a filename to process, and this function
432is non-NULL, it will call this to run the file. For file formats such as
433PDF (which need to be buffered so they can be read out of order), this
434can avoid the need to feed in all the data via <code>proc_process</code>,
435buffer it somewhere, and then process it at the end.</p>
436<p>For many languages this can be <code>NULL</code>.</p>
437<h3><a name="proc_process_begin"></a>proc_process_begin</h3>
438<pre>
439typedef int (*pl_interp_proc_process_begin_t) (pl_interp_implementation_t *);
440</pre>
441<p>Once the GPDL library has data to process (that it cannot process
442with <code>proc_process_file</code>, it will call this function to
443setup the transfer of data.</p>
444<h3><a name="proc_process"></a>proc_process</h3>
445<pre>
446typedef int (*pl_interp_proc_process_t) (pl_interp_implementation_t *,
447 stream_cursor_read *);
448</pre>
449<p>After the GPDL library has called <code>proc_process_begin</code>
450this function may be called multiple times to actually transfer
451the data in. The implementation is expected to consume as many bytes
452as it can (but not necessarily all of them) before returning with
453an updated read pointer. If this function cannot advance without more
454data, it should return with <code>gs_error_NeedInput</code>.</p>
455<h3><a name="proc_process_end"></a>proc_process_end</h3>
456<pre>
457typedef int (*pl_interp_proc_process_end_t) (pl_interp_implementation_t *);
458</pre>
459<p>After the GPDL library has called <code>proc_process_begin</code>
460(and possibly made a number of calls to <code>proc_process</code>) it
461will call <code>proc_process_end</code> to signify the end of the
462data. This does not necessarily signal the end of the job.</p>
463<h3><a name="proc_flush_to_eoj"></a>proc_flush_to_eoj</h3>
464<pre>
465typedef int (*pl_interp_proc_flush_to_eoj_t) (pl_interp_implementation_t *,
466 stream_cursor_read *);
467</pre>
468<p>In the event of a problem while processing data, GPDL may seek to
469abandon processing of a transfer in progress by calling
470<code>proc_flush_to_eoj</code>. If possible, the language should
471continue to process data until a reasonable stopping point, or until
472<code>UEL</code> is reached.</p>
473<h3><a name="proc_process_eof"></a>proc_process_eof</h3>
474<pre>
475typedef int (*pl_interp_proc_process_eof_t) (pl_interp_implementation_t *);
476</pre>
477<p>Called when GPDL reaches EOF in processing a job. A language
478implementation should assume no more data is forthcoming.</p>
479<h3><a name="proc_report_errors"></a>proc_report_errors</h3>
480<pre>
481typedef int (*pl_interp_proc_report_errors_t) (pl_interp_implementation_t *,
482 int,
483 long,
484 bool);
485</pre>
486<p>Called after running a job to give the language implementation the
487chance to report any errors it may have detected as it ran.</p>
488<h3><a name="proc_dnit_job"></a>proc_dnit_job</h3>
489<pre>
490typedef int (*pl_interp_proc_dnit_job_t) (pl_interp_implementation_t *);
491</pre>
492<p>Called after a job is complete so that the language implementation
493may clean up. The interpreter is kept open so that more jobs can be
494fed to it, but no state should be kept from this job to the next.</p>
495<h3><a name="proc_deallocate_interp_instance"></a>proc_deallocate_interp_instance</h3>
496<pre>
497typedef int (*pl_interp_proc_deallocate_interp_instance_t)
498 (pl_interp_implementation_t *);
499</pre>
500<p>Called on shutdown of the GPDL library to close down the language
501instance and free all the resources.</p>
502
503<!-- [2.0 end contents] ==================================================== -->
504<!-- [3.0 begin visible trailer] =========================================== -->
505<hr>
506<p>
507<small>Copyright &copy; 2000-2020 Artifex Software, Inc. All rights reserved.</small></p>
508
509<p>
510This software is provided AS-IS with no warranty, either express or
511implied.</p>
512
513This software is distributed under license and may not be copied, modified
514or distributed except as expressly authorized under the terms of that
515license. Refer to licensing information at <a href="https://www.artifex.com">https://www.artifex.com</a>
516or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200,
517Novato, CA 94945, U.S.A., +1(415)492-9861, for further information.</p>
518
519<p>
520<small>Ghostscript version 9.23, 21 March 2018</p>
521
522<!-- [3.0 end visible trailer] ============================================= -->
523
524<!--FINISH EDITING HERE-->
525
526 </div>
527 </div>
528 </div>
529
530 <div class="footer">
531 <div class="row">
532 <div class="col-7 footleft">
533 <ul>
534 <li><a href="https://artifex.com/contact-us/" target="blank">CONTACT US</a></li>
535 <li><a href="https://artifex.com/about-us/" target="blank">ABOUT</a></li>
536 <li><a href="https://ghostscript.com/security.html">SECURITY</a></li>
537 </ul>
538 </div>
539 <div class="col-1 footcenter">
540 <ul>
541 <li><a href="https://artifex.com/support/" target="blank">SUPPORT</a></li>
542 <li><a href="https://artifex.com/blog/artifex/" target="blank">BLOG</a></li>
543 <li><a href="https://artifex.com/privacy-policy/" target="blank">PRIVACY</a></li>
544 </ul>
545 </div>
546 <div class="col-ft-3 footright"><img src="images/Artifex_logo.png" width="194" height="40" alt=""/> <br>
547 © Copyright 2019 Artifex Software, Inc. <br>
548 All rights reserved.
549 </div>
550 </div>
551 </div>
552
553 <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
554 <script src="index.js"></script>
555</body>
556</html>