README 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. This directory holds the source files for a C/C++ 2-D vector graphics
  2. library: GNU libplot/libplotter. It is distributed under the GNU GPL
  3. (see the file ../COPYING).
  4. The library provides a uniform interface to numerous display devices and
  5. output formats. The interface presents each output device as a opaque
  6. `Plotter' object: a virtual pen plotter. Each of libplot's output drivers
  7. is accessed at run time through an Plotter object of the appropriate type.
  8. Many types of Plotter are supported: Metafile, Tektronix, ReGIS, HP-GL/2,
  9. PCL 5, xfig, CGM (by default, WebCGM), idraw-editable Postscript, Adobe
  10. Illustrator, SVG, GIF, PNM (i.e. PBM/PGM/PPM), PNG, X Drawable, and X. All
  11. but the final two write graphic output to an output stream. The final two
  12. draw graphics by calling X routines. The X Drawable driver draws graphics
  13. in one or two `drawables' (windows or pixmaps), which must be passed to it
  14. via pointers, as Plotter parameters. The X driver pops up a window
  15. (actually, a Label widget) on an X display, and draws graphics in it.
  16. The names of the source files include as prefix a letter indicating which
  17. driver they include code for. For example, the file h_openpl.c is part of
  18. the HP-GL/2 driver, and contains low-level code which is called when the
  19. API function pl_openpl_r() is invoked on an HP-GL/2 or PCL 5 Plotter.
  20. Namely, it contains the _pl_h_begin_page() function. As prefixes,
  21. m=Metafile, t=Tektronix, r=ReGIS, h=HP-GL[/2] and PCL 5, f=Fig, c=CGM,
  22. p=Postscript, a=Adobe Illustrator, s=SVG, i=GIF, n=PNM, z=PNG, x=XDrawable,
  23. and y=X. (Actually, XPlotters use mostly XDrawablePlotter methods.) The
  24. many files whose names begin with `g' (e.g., g_relative.c) contain generic
  25. code, i.e. code used by the base Plotter class from which all other classes
  26. are derived. Similarly, `b' files contain code for a generic bitmap
  27. Plotter class, from which the PNM and PNG Plotter classes are derived.
  28. The library comes in two versions: libplot, which provides two C APIs (an
  29. old non-thread-safe API, and a new thread-safe API), and libplotter, which
  30. is a full-fledged C++ class library. They are compiled in separate
  31. directories (respectively, in this directory and in ../libplotter).
  32. However, the same source files are used in both libraries. That is a bit
  33. remarkable. It is arranged in the the two key header files
  34. ../include/plotter.h and ./extern.h. If LIBPLOTTER is defined and a C++
  35. compiler is used, a C++ class library results. Otherwise, a conventional C
  36. function library results. There are a lot of conditionalizations in those
  37. two header files.
  38. In libplot, a Plotter is implemented as a C-style struct. Each such struct
  39. includes approximately 20 function pointers: one function pointer for each
  40. of the low-level functions that define the semantics of the Plotter. It is
  41. by dereferencing these function pointers that polymorphism is implemented
  42. in libplot. The initialization of the `function pointer part' of each
  43. Plotter is contained in the corresponding ?_defplot.c file. At present,
  44. the low-level functions include
  45. initialize
  46. terminate
  47. begin_page
  48. erase_page
  49. end_page
  50. push_state
  51. pop_state
  52. paint_path
  53. paint_paths
  54. path_is_flushable
  55. maybe_prepaint_segments
  56. paint_marker
  57. paint_point
  58. paint_text_string_with_escapes
  59. paint_text_string
  60. get_text_width
  61. retrieve_font
  62. flush_output
  63. warning
  64. error
  65. For an explanation of the functionality of each of the above, see the
  66. comments in ../include/plotter.h.
  67. Here are some more details on how polymorphism is implemented. In libplot,
  68. each API function or internal Plotter method takes a pointer to a Plotter
  69. struct as its first argument. In the code, this pointer argument is always
  70. written as `_plotter'. It's the function pointers in `_plotter' that are
  71. dereferenced, when performing any low-level operation. For example, in the
  72. libplot code you may see a function invocation like
  73. `_plotter->begin_page(...)'. This will (1) dereference the `_plotter'
  74. pointer to get a Plotter, and (2) dereference the function pointer in the
  75. Plotter to find the Plotter-specific implementation of the low-level
  76. operation `begin_page()'. This would be _pl_h_begin_page() if `_plotter'
  77. points to an HP-GL/2 or PCL Plotter, for example.
  78. The two special files apinewc.c and apioldc.c, which alone among the source
  79. files are included in libplot but not libplotter, define the libplot API.
  80. Each function in the new (thread-safe) API takes as first argument a
  81. pointer to a Plotter struct to which the Plotter operations should be
  82. applied. It is this pointer which is passed down to the low-level code as
  83. `_plotter'. Each function in the old (non-thread-safe) API acts on a
  84. Plotter which is globally selected by calling the function pl_selectpl().
  85. Both old and new C APIs include functions like pl_newpl[_r]() and
  86. pl_deletepl[_r](), which create and destroy Plotters.
  87. The C++ binding, i.e. the libplotter C++ class library, is easier to
  88. understand. There is a generic Plotter class and derived classes that are
  89. declared in ../include/plotter.h. These classes include as public members
  90. the API functions, such as openpl(), and low-level device-specific
  91. functions, such as begin_page(), which are protected and virtual. All this
  92. is arranged by a *lot* of conditional #defines in ../include/extern.h. For
  93. example, if LIBPLOTTER is defined then the internal function _h_begin_page
  94. is redefined to be HPGLPlotter::begin_page, which overrides the generic
  95. begin_page method, which is a no-op.
  96. Also, if LIBPLOTTER is defined then the Plotter methods don't all have a
  97. special first argument called `_plotter'. By the magic of the C/C++
  98. preprocessor, the first argument disappears. And in the code for each
  99. method, `_plotter' is redefined to be `this', i.e. a pointer to the Plotter
  100. whose operation is being invoked. Believe it or not, it works: the same
  101. source files can be used in the compilation of both libplot and libplotter.
  102. A further implementation note: in libplot, any Plotter contains a `tag
  103. field', identifying its type (X11, PS, PCL5, etc.). This tag field is used
  104. only in libplot, and only by a very few forwarding functions, so if
  105. -DLIBPLOTTER is used, it's #ifdef'd out. (See ../include/plotter.h.)
  106. Other than that, the data members are essentially the same in the libplot
  107. Plotter struct as they are in the libplotter Plotter class.
  108. Actually, it's a bit more complicated. In libplot, each Plotter struct, no
  109. matter what its type, contains essentially the same data members. But
  110. there are a lot of data members, some of them which are relevant to all
  111. Plotters (e.g., `line_type') and some of which are specific to individual
  112. types of Plotter (e.g., `hpgl_line_type', which keeps track of an HP-GL
  113. display device's internal state). That means there's a lot of redundancy
  114. (an XPlotter struct, for example, contains all the private data members
  115. that an HPGLPlotter uses, but never uses them). In libplotter, the design
  116. is cleaner: each of the many data members which in libplot are contained in
  117. every Plotter struct is moved to the appropriate derived class. So
  118. `hpgl_line_type' is a data member of the HPGLPlotter class, but not of the
  119. base (generic) Plotter class, or any of its other subclasses. That's
  120. arranged by extensive #ifdef's in ../include/plotter.h. It's a bit awkward
  121. though: each data member needs to be declared twice in that file.
  122. Adding support for a new type of display device or output file format,
  123. given the object-oriented way that libplot/libplotter is structured, is
  124. easy. A new type of Plotter, derived from the base Plotter class, would be
  125. declared in ../include/plotter.h. If it needs any private data members,
  126. which it probably will, they would be declared in two places in that file,
  127. as just mentioned. Also, conditional #defines for the methods used by the
  128. new Plotter (to distinguish libplot from libplotter) would be added at the
  129. end of ./extern.h.
  130. A `defplot' file for the new Plotter type would need to be written too.
  131. Besides low-level initialize() and terminate() routines for the new Plotter
  132. type, it would include, for the benefit of libplot, an initialization
  133. routine for the part of the Plotter struct that contains low-level
  134. device-specific functions. (See, for example, the initializing structure
  135. _pl_h_default_plotter in the file h_defplot.c, as well as the routines
  136. _pl_h_initialize and _pl_h_terminate.) The new Plotter initialization
  137. would need to be added to the _plotter_data[] table in apinewc.c, which is
  138. specific to libplot. No such initialization is required for libplotter,
  139. since the C++ compiler itself initializes any instance of a derived Plotter
  140. class.
  141. To see how easy it is to add or remove support for a Plotter type, search
  142. for the symbol X_DISPLAY_MISSING. If this is defined, support for X
  143. Drawable Plotters and X Plotters will be dropped at compile time. The only
  144. occurrences in the code of tests like `#ifdef X_DISPLAY_MISSING' are in the
  145. header files ../include/plotter.h and ./extern.h, in apinewc.c, and in
  146. g_defstate.c. And the only reason for the appearance of X_DISPLAY_MISSING
  147. in g_defstate.c is that the drawing state structure initialization located
  148. in that file contains some X-specific fields, which need to be omitted if X
  149. support is dropped. That's because they rely on symbols defined in X11
  150. header files.
  151. Up to now we haven't mentioned drawing states, but every Plotter includes a
  152. pointer to a stack of them. Drawing states, which are structs in both
  153. libplot and libplotter, contain numerous fields that are specific to
  154. individual types of Plotter. The reason device-specific information is
  155. kept in the drawing state struct is that it's convenient. For example,
  156. when the user-frame line width is changed, the device-frame line width
  157. changes too. Rather than compute the device-frame line width each time a
  158. graphical object is drawn, it's easier to store it in the drawing state.
  159. There are many other such examples (see the declaration of the drawing
  160. state structure as the `plDrawstate' typedef in ../include/plotter.h).