mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-05-01 16:07:57 +02:00
doc/public/language-bindings.xml doc/public/cairo-doc.xml doc/public/Makefile.am: Document suggested conventions and techniques for many aspects of creating a language binding for Cairo.
This commit is contained in:
parent
35248a17fd
commit
2e72b70124
2 changed files with 733 additions and 0 deletions
|
|
@ -1,3 +1,10 @@
|
|||
2005-05-07 Owen Taylor <otaylor@redhat.com>
|
||||
|
||||
* doc/public/language-bindings.xml doc/public/cairo-doc.xml
|
||||
doc/public/Makefile.am: Document suggested conventions and
|
||||
techniques for many aspects of creating a language binding
|
||||
for Cairo.
|
||||
|
||||
2005-05-07 Owen Taylor <otaylor@redhat.com>
|
||||
|
||||
* doc/public/cairo-sections.txt: Update.
|
||||
|
|
|
|||
726
doc/public/language-bindings.xml
Normal file
726
doc/public/language-bindings.xml
Normal file
|
|
@ -0,0 +1,726 @@
|
|||
<appendix id="language-bindings">
|
||||
<title>Creating a language binding for cairo</title>
|
||||
<para>
|
||||
While cairo is implemented and C, and has a C API, it is expected
|
||||
that many users of cairo will be using it from languages other
|
||||
than C. The glue that connects the core cairo library to another
|
||||
language is known as a <firstterm>language
|
||||
binding</firstterm>. This appendix attempts to collect together
|
||||
issues that come up when creating a language bindings for cairo
|
||||
and present standardized solutions to promote consistency among
|
||||
the different language bindings.
|
||||
</para>
|
||||
<sect1 id="bindings-general">
|
||||
<title>General considerations</title>
|
||||
<para>
|
||||
The naming of the central <link
|
||||
linkend="cairo-t"><type>cairo_t</type></link> type is a
|
||||
special exception. The object is “a cairo context” not “a
|
||||
cairo”, and names such as <type>cairo_t</type> rather than
|
||||
<type>cairo_context_t</type> and
|
||||
<function>cairo_set_source()</function> rather than
|
||||
<function>cairo_context_set_source()</function> are simply
|
||||
abbreviations to make the C API more palatable. In languages
|
||||
which have object-oriented syntax, this abbreviation is much
|
||||
less useful. In fact, if ‘Cairo’ is used as a namespace, then
|
||||
in many languages, you'd end up with a ridiculous type name
|
||||
like ‘Cairo.Cairo’. For this reason, and for inter-language
|
||||
consistency all object-oriented languages should name this
|
||||
type as if it were <type>cairo_context_t</type>.
|
||||
</para>
|
||||
<para>
|
||||
The punctuation and casing of the type names and
|
||||
method names of cairo should be changed to match the general
|
||||
convention of the language. In Java, where type names are written
|
||||
in StudlyCaps and method names in javaCaps, cairo_font_extents_t
|
||||
will become FontExtents and
|
||||
<literal>cairo_set_source(cr,source)</literal>,
|
||||
<literal>cr.setSource(source)</literal>.
|
||||
As compared to changing the punctuation, and casing, much
|
||||
more reluctance should be used in changing the method names
|
||||
themselves. Even if get is usually omitted from getters in
|
||||
your language, you shouldn't bind cairo_get_source() as
|
||||
cr.source().
|
||||
</para>
|
||||
</sect1>
|
||||
<sect1 id="bindings-memory">
|
||||
<title>Memory Management</title>
|
||||
<para>
|
||||
The objects in cairo can roughly be divided into two types:
|
||||
refcounted opaque types like
|
||||
<link
|
||||
linkend="cairo-surface-t"><type>cairo_surface_t</type></link>
|
||||
and plain structures like
|
||||
<link
|
||||
linkend="cairo-glyph-t"><type>cairo_glyph_t</type></link>.
|
||||
<link
|
||||
linkend="cairo-path-t"><type>cairo_path_t</type></link>
|
||||
and
|
||||
<link
|
||||
linkend="cairo-path-data-t"><type>cairo_path_data_t</type></link>
|
||||
are special cases and are treated separately in this appendix.
|
||||
</para>
|
||||
<para>
|
||||
Refcounted opaque types all have a
|
||||
<function>..._reference()</function>
|
||||
function to increase the refcount by one and a
|
||||
<function>..._destroy()</function> to decrease the refcount
|
||||
by one. These should not be exposed to the user of the language
|
||||
binding, but rather used to implement memory management within
|
||||
the language binding. The simplest way to do memory management
|
||||
for a language binding is to treat the language binding object
|
||||
as a simple handle to the cairo object. The language binding
|
||||
object references the cairo object, and unreferences it when
|
||||
finalized. This is the recommended method, though there are
|
||||
a couple of caveats to be noted:
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
Equality won't work as expected. You can have two language
|
||||
objects for the same cairo and they won't necessarily
|
||||
compare equal. If the language allows customizing the
|
||||
equality operation, then this is fixable by comparing
|
||||
the underlying pointers. It also can be fixed by creating
|
||||
at most one language object per cairo object, and
|
||||
uniquifying via a <firstterm>pin table</firstterm> (a hash
|
||||
table that goes from cairo object to language object).
|
||||
For <type>cairo_surface_t</type> you can use also
|
||||
<link
|
||||
linkend="cairo-surface-set-user-data"><function>cairo_surface_set_user_data()</function></link>
|
||||
instead of a separate pin table.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Derivation from the language object doesn't work because
|
||||
you can lose the language object while keeping the Cairo
|
||||
object. Code like:
|
||||
</para>
|
||||
<programlisting>
|
||||
public class MySurface (ImageSurface) {
|
||||
public MySurface (width, height) {
|
||||
super (Format.ARGB32, width, height);
|
||||
}
|
||||
public int get42 () {
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
|
||||
cr = Cairo(MySurface(width, height));
|
||||
surface = cr.getTarget();
|
||||
</programlisting>
|
||||
<para>
|
||||
Can result in <varname>surface</varname> containing an
|
||||
<classname>ImageSurface</classname> not a <classname>MySurface</classname>.
|
||||
This is not easily fixable without creating memory leaks,
|
||||
and it's probably best to simply forbid deriving from the
|
||||
language objects.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
<para>
|
||||
When a plain structure is used as a return value from cairo,
|
||||
this is done by passing it as a “out parameter”.
|
||||
</para>
|
||||
<programlisting>
|
||||
cairo_font_extents_t extents;
|
||||
|
||||
cairo_font_extents (cr, &extents);</programlisting>
|
||||
<para>
|
||||
In a language binding, this should typically be treated
|
||||
as a return value:
|
||||
</para>
|
||||
<programlisting>
|
||||
FontExtents extents = cr.fontExtents ();</programlisting>
|
||||
<para>
|
||||
A language binding has a choice in how it implements the
|
||||
language objects for plain structures. It can use a pure
|
||||
language object with fields corresponding to those of the C
|
||||
structure, and convert from and to the C structure when calling
|
||||
cairo functions or converting cairo return values. Or it
|
||||
can keep a pointer to the C structure internally and wrap
|
||||
it inside a language object much like occurs for refcounted
|
||||
objects. The choice should be invisible to the user: they should
|
||||
be able to imagine that it is implemented as a pure language
|
||||
object.
|
||||
</para>
|
||||
</sect1>
|
||||
<sect1 id="bindings-return-values">
|
||||
<title>Multiple return values</title>
|
||||
<para>
|
||||
There are a number of functions in the cairo API that have
|
||||
multiple <firstterm>out parameters</firstterm> or
|
||||
<firstterm>in-out parameters</firstterm>. In some languages
|
||||
these can be translated into multiple return values. In Python,
|
||||
what is:
|
||||
</para>
|
||||
<programlisting>
|
||||
cairo_user_to_device (cr, &x, &y);</programlisting>
|
||||
<para>
|
||||
can by mapped to:
|
||||
</para>
|
||||
<programlisting>
|
||||
(x, y) = cr.user_to_device (cr, x, y);</programlisting>
|
||||
<para>
|
||||
but many languages don't have provisions for multiple return
|
||||
values, so it is necessary to introduce auxiliary types.
|
||||
Most of the functions that require the auxiliary types
|
||||
require a type that would, in C, look like
|
||||
</para>
|
||||
<programlisting>
|
||||
typedef struct _cairo_point cairo_point_t;
|
||||
struct _cairo_point {
|
||||
double x;
|
||||
double y;
|
||||
}</programlisting>
|
||||
<para>
|
||||
The same type should be used both for functions that use a pair
|
||||
of coordinates as an absolute position, and functions that use
|
||||
a pair of coordinates as a displacement. While an argument could
|
||||
be made that having a separate “distance” type is more correct,
|
||||
it is more likely just to confuse users.
|
||||
</para>
|
||||
<programlisting>
|
||||
void
|
||||
cairo_user_to_device (cairo_t *cr, double *x, double *y);
|
||||
|
||||
void
|
||||
cairo_user_to_device_distance (cairo_t *cr, double *dx, double *dy);
|
||||
|
||||
void
|
||||
cairo_device_to_user (cairo_t *cr, double *x, double *y);
|
||||
|
||||
void
|
||||
cairo_device_to_user_distance (cairo_t *cr, double *dx, double *dy);
|
||||
|
||||
void
|
||||
cairo_matrix_transform_distance (cairo_matrix_t *matrix, double *dx, double *dy);
|
||||
|
||||
void
|
||||
cairo_matrix_transform_point (cairo_matrix_t *matrix, double *x, double *y);
|
||||
|
||||
void
|
||||
cairo_get_current_point (cairo_t *cr, double *x, double *y);
|
||||
</programlisting>
|
||||
<para>
|
||||
There are also a couple of functions that return four values
|
||||
representing a rectangle. These should be mapped to a
|
||||
“rectangle” type that looks like:
|
||||
</para>
|
||||
<programlisting>
|
||||
typedef struct _cairo_rectangle cairo_rectangle_t;
|
||||
struct _cairo_rectangle {
|
||||
double x;
|
||||
double y;
|
||||
double width;
|
||||
double height;
|
||||
}</programlisting>
|
||||
<para>
|
||||
The C function returns the rectangle as a set of two points to
|
||||
facilitate rounding to integral extents, but this isn't worth
|
||||
adding a “box” type to go along with the more obvious
|
||||
“rectangle” representation.
|
||||
</para>
|
||||
<remark>
|
||||
Q: Would it make sense here to define a standard
|
||||
<function>cairo_rectangle_round()</function> method
|
||||
that language bindings should map?
|
||||
</remark>
|
||||
<programlisting>
|
||||
void
|
||||
cairo_stroke_extents (cairo_t *cr,
|
||||
double *x1, double *y1,
|
||||
double *x2, double *y2);
|
||||
|
||||
void
|
||||
cairo_fill_extents (cairo_t *cr,
|
||||
double *x1, double *y1,
|
||||
double *x2, double *y2);
|
||||
</programlisting>
|
||||
</sect1>
|
||||
<sect1 id="bindings-overloading">
|
||||
<title>Overloading and optional arguments</title>
|
||||
<para>
|
||||
Function overloading (having a several variants of a function
|
||||
with the same name and different arguments) is a language
|
||||
feature available in many languages but not in C.
|
||||
</para>
|
||||
<para>
|
||||
In general, language binding authors should use restraint in
|
||||
combining functions in the cairo API via function
|
||||
overloading. What may seem like an obvious overload now may
|
||||
turn out to be strange with future additions to cairo.
|
||||
It might seem logical to make
|
||||
<link
|
||||
linkend="cairo-set-source-rgb"><function>cairo_set_source_rgb()</function></link>
|
||||
an overload of <function>cairo_set_source()</function>, but future plans to add
|
||||
<function>cairo_set_source_rgb_premultiplied()</function>,
|
||||
which will also take three doubles make this a bad idea. For
|
||||
this reason, only the following pairs of functions should
|
||||
be combined via overloading
|
||||
</para>
|
||||
<programlisting>
|
||||
void
|
||||
cairo_set_source (cairo_t *cr, cairo_pattern_t *source);
|
||||
|
||||
void
|
||||
cairo_set_source_surface (cairo_t *cr,
|
||||
cairo_surface_t *source,
|
||||
double surface_x,
|
||||
double surface_y);
|
||||
|
||||
void
|
||||
cairo_mask (cairo_t *cr,
|
||||
cairo_pattern_t *pattern);
|
||||
|
||||
void
|
||||
cairo_mask_surface (cairo_t *cr,
|
||||
cairo_surface_t *surface,
|
||||
double surface_x,
|
||||
double surface_y);
|
||||
|
||||
cairo_surface_t *
|
||||
cairo_image_surface_create (cairo_format_t format,
|
||||
int width,
|
||||
int height);
|
||||
cairo_surface_t *
|
||||
cairo_image_surface_create_for_data (unsigned char *data,
|
||||
cairo_format_t format,
|
||||
int width,
|
||||
int height,
|
||||
int stride);
|
||||
|
||||
cairo_status_t
|
||||
cairo_surface_write_to_png (cairo_surface_t *surface,
|
||||
const char *filename);
|
||||
|
||||
cairo_status_t
|
||||
cairo_surface_write_to_png_stream (cairo_surface_t *surface,
|
||||
cairo_write_func_t write_func,
|
||||
void *closure);
|
||||
|
||||
cairo_surface_t *
|
||||
cairo_image_surface_create_from_png (const char *filename);
|
||||
|
||||
cairo_surface_t *
|
||||
cairo_image_surface_create_from_png_stream (cairo_read_func_t read_func,
|
||||
void *closure);
|
||||
</programlisting>
|
||||
<para>
|
||||
Note that there are cases where all constructors for a type
|
||||
aren't overloaded together. For example
|
||||
<link
|
||||
linkend="cairo-image-surface-create-from-png"><function>cairo_image_surface_create_from_png()</function></link>
|
||||
should <emphasis>not</emphasis> be overloaded together with
|
||||
<link
|
||||
linkend="cairo-image-surface-create"><function>cairo_image_surface_create()</function></link>.
|
||||
In such cases, the remaining constructors will typically need to
|
||||
be bound as static methods. In Java, for example, we might have:
|
||||
</para>
|
||||
<programlisting>
|
||||
Surface surface1 = ImageSurface(Format.RGB24, 100, 100);
|
||||
Surface surface2 = ImageSurface.createFromPNG("camera.png");</programlisting>
|
||||
<para>
|
||||
Some other overloads that add combinations not found in C may be
|
||||
convenient for users for language bindings that provide
|
||||
<type>cairo_point_t</type> and <type>cairo_rectangle_t</type>
|
||||
types, for example:
|
||||
</para>
|
||||
<programlisting>
|
||||
void
|
||||
cairo_move_to (cairo_t *cr,
|
||||
cairo_point_t *point);
|
||||
void
|
||||
cairo_rectangle (cairo_t *cr,
|
||||
cairo_rectangle_t *rectangle);
|
||||
</programlisting>
|
||||
</sect1>
|
||||
<sect1 id="bindings-streams">
|
||||
<title>Streams and File I/O</title>
|
||||
<para>
|
||||
Various places in the cairo API deal with reading and writing
|
||||
data, whether from and to files, or to other sources and
|
||||
destinations. In these cases, what is typically provided in the
|
||||
C API is a simple version that just takes a filename, and a
|
||||
complex version that takes a callback function.
|
||||
An example is the PNG handling functions:
|
||||
</para>
|
||||
<programlisting>
|
||||
cairo_surface_t *
|
||||
cairo_image_surface_create_from_png (const char *filename);
|
||||
|
||||
cairo_surface_t *
|
||||
cairo_image_surface_create_from_png_stream (cairo_read_func_t read_func,
|
||||
void *closure);
|
||||
|
||||
cairo_status_t
|
||||
cairo_surface_write_to_png (cairo_surface_t *surface,
|
||||
const char *filename);
|
||||
|
||||
cairo_status_t
|
||||
cairo_surface_write_to_png_stream (cairo_surface_t *surface,
|
||||
cairo_write_func_t write_func,
|
||||
void *closure);</programlisting>
|
||||
<para>
|
||||
The expectation is that the filename version will be mapped
|
||||
literally in the language binding, but the callback version
|
||||
will be mapped to a version that takes a language stream
|
||||
object. For example, in Java, the four functions above
|
||||
might be mapped to:
|
||||
</para>
|
||||
<programlisting>
|
||||
static public ImageSurface createFromPNG (String filename) throws IOException;
|
||||
static public ImageSurface createFromPNG (InputStream stream) throws IOException;
|
||||
public void writeToPNG (String filename) throws IOException;
|
||||
public void writeToPNG (OutputStream stream) throws IOException;
|
||||
</programlisting>
|
||||
<para>
|
||||
In many cases, it will be better to
|
||||
implement the filename version internally
|
||||
using the stream version, rather than building it on top of the
|
||||
filename version in C. The reason for this is that will
|
||||
naturally give a more standard handling of file errors for
|
||||
the language, as seen in the above Java example, where
|
||||
<methodname>createFromPNG()</methodname> is marked as raising
|
||||
an exception. Propagating exceptions from inside the callback
|
||||
function to the caller will pose a challenge to the language
|
||||
binding implementor, since an exception must not propagate
|
||||
through the Cairo code. A technique that will be useful in
|
||||
some cases is to catch the exception in the callback,
|
||||
store the exception object inside a structure pointed to by
|
||||
<parameter>closure</parameter>, and then rethrow it once
|
||||
the function returns.
|
||||
</para>
|
||||
<remark>
|
||||
I'm not sure how to handle this for
|
||||
<link
|
||||
linkend="cairo-pdf-surface-create-for-callback"><function>cairo_pdf_surface_create_for_callback()</function></link>.
|
||||
Other than keep a “exception to rethrow” thread-specific
|
||||
variable
|
||||
that is checked after <emphasis>every</emphasis> call to a Cairo
|
||||
function.
|
||||
</remark>
|
||||
</sect1>
|
||||
<sect1 id="bindings-errors">
|
||||
<title>Error handling</title>
|
||||
<para>
|
||||
The error handling approach in C for Cairo has multiple
|
||||
elements:
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem><para>
|
||||
When a method on an object fails, the object is put into
|
||||
an error state. Subsequent operations on the object do
|
||||
nothing. The status of the object can be queried with
|
||||
a function like <link
|
||||
linkend="cairo-status"><function>status()</function></link>.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
Constructors, rather than
|
||||
returning<constant>NULL</constant> on out-of-memory failure,
|
||||
return a special singleton object on which all
|
||||
operations do nothing. Retrieving the status of the
|
||||
singleton object returns <constant>CAIRO_STATUS_NO_MEMORY</constant>
|
||||
</para>
|
||||
<remark>
|
||||
Is this going to apply to
|
||||
<type>cairo_surface_t</type> as well?
|
||||
</remark>
|
||||
<remark>
|
||||
What about cairo_copy_path_data()? It's probably going to
|
||||
have to return <constant>NULL</constant>.
|
||||
</remark>
|
||||
</listitem>
|
||||
<listitem><para>
|
||||
Errors propagate from object to object. Setting a pattern
|
||||
in an out-of-memory state as the source of a
|
||||
<type>cairo_t</type> puts the type into an error state.
|
||||
</para></listitem>
|
||||
</itemizedlist>
|
||||
<remark>Much of the above is not yet implemented at the time of
|
||||
this writing</remark>
|
||||
<para>
|
||||
A language binding could copy the C approach, and for a
|
||||
language without exceptions, this is likely the right thing
|
||||
to do. However, for a language with exceptions, exposing
|
||||
a completely different style of error handling for cairo
|
||||
would be strange. So, instead, status should be checked
|
||||
after every call to cairo, and exceptions thrown as necessary.
|
||||
</para>
|
||||
<para>
|
||||
One problem that can arise with this, in languages
|
||||
where handling exceptions is mandatory (like Java), is that almost
|
||||
every cairo function can result in a status being set,
|
||||
usually because of an out-of-memory condition. This could make
|
||||
cairo hard to use. To resolve this problem, let's classify then
|
||||
cairo status codes:
|
||||
</para>
|
||||
<programlisting>
|
||||
/* Memory */
|
||||
CAIRO_STATUS_NO_MEMORY,
|
||||
|
||||
/* Programmer error */
|
||||
CAIRO_STATUS_INVALID_RESTORE
|
||||
CAIRO_STATUS_INVALID_POP_GROUP
|
||||
CAIRO_STATUS_NO_CURRENT_POINT
|
||||
CAIRO_STATUS_INVALID_MATRIX
|
||||
CAIRO_STATUS_NO_TARGET_SURFACE
|
||||
CAIRO_STATUS_INVALID_STRING
|
||||
CAIRO_STATUS_SURFACE_FINISHED
|
||||
CAIRO_STATUS_BAD_NESTING
|
||||
|
||||
/* Language binding implementation */
|
||||
CAIRO_STATUS_NULL_POINTER
|
||||
CAIRO_STATUS_INVALID_PATH_DATA
|
||||
CAIRO_STATUS_SURFACE_TYPE_MISMATCH
|
||||
|
||||
/* Other */
|
||||
CAIRO_STATUS_READ_ERROR
|
||||
CAIRO_STATUS_WRITE_ERROR
|
||||
</programlisting>
|
||||
<para>
|
||||
If we look at these, the
|
||||
<constant>CAIRO_STATUS_NO_MEMORY</constant>
|
||||
should map to the native out-of-memory exception, which could
|
||||
happen at any point in any case. Most of the others indicate
|
||||
programmer error, and handling them in user code would be
|
||||
silly. These should be mapped into whatever the language uses
|
||||
for assertion failures, rather than errors that are normally
|
||||
handled. (In Java, a subclass of Error rather than Exception,
|
||||
perhaps.) And <constant>CAIRO_STATUS_READ_ERROR</constant>,
|
||||
and <constant>CAIRO_STATUS_WRITE_ERROR</constant> can occur
|
||||
only in very specific places. (In fact, as described
|
||||
in <xref linkend="bindings-streams"/>, these errors may be
|
||||
mapped into the language's native I/O error types.)
|
||||
So, there really aren't exceptions that the programmer must
|
||||
handle at most points in the Cairo API.
|
||||
</para>
|
||||
</sect1>
|
||||
<sect1 id="bindings-patterns">
|
||||
<title>Patterns</title>
|
||||
<para>
|
||||
The cairo C API allows for creating a number of different types
|
||||
of patterns. All of these different types of patterns map to
|
||||
<link
|
||||
linkend="cairo-pattern-t"><type>cairo_pattern_t</type></link>
|
||||
in C, but in an object oriented language, there should instead
|
||||
be a hierarchy of types. (The functions that should map to
|
||||
constructors for the various types are listed after the type,
|
||||
methods on that type are listed below)
|
||||
</para>
|
||||
<programlisting>
|
||||
cairo_pattern_t
|
||||
<link linkend="cairo-pattern-set-matrix"><function>cairo_pattern_set_matrix()</function></link>
|
||||
<link linkend="cairo-pattern-get-matrix"><function>cairo_pattern_get_matrix()</function></link>
|
||||
cairo_solid_pattern_t
|
||||
cairo_surface_pattern_t (<link linkend="cairo-pattern-create-for-surface"><function>cairo_pattern_create_for_surface()</function></link>)
|
||||
<link linkend="cairo-pattern-set-extend"><function>cairo_pattern_set_extend()</function></link>
|
||||
<link linkend="cairo-pattern-get-extend"><function>cairo_pattern_get_extend()</function></link>
|
||||
<link linkend="cairo-pattern-set-filter"><function>cairo_pattern_set_filter()</function></link>
|
||||
<link linkend="cairo-pattern-get-filter"><function>cairo_pattern_get_filter()</function></link>
|
||||
cairo_gradient_t
|
||||
<link linkend="cairo-pattern-add-color-stop-rgb"><function>cairo_pattern_add_color_stop_rgb()</function></link>
|
||||
<link linkend="cairo-pattern-add-color-stop-rgba"><function>cairo_pattern_add_color_stop_rgba()</function></link>
|
||||
cairo_linear_gradient_t (<link linkend="cairo-pattern-create-linear"><function>cairo_pattern_create_linear()</function></link>)
|
||||
cairo_radial_gradient_t (<link linkend="cairo-pattern-create-radial"><function>cairo_pattern_create_radial()</function></link>)
|
||||
</programlisting>
|
||||
<para>
|
||||
</para>
|
||||
</sect1>
|
||||
<sect1 id="bindings-surfaces">
|
||||
<title>Surfaces</title>
|
||||
<para>
|
||||
Like patterns, surfaces, which use only the
|
||||
<link
|
||||
linkend="cairo-surface-t"><type>cairo_surface_t</type></link>
|
||||
type in the C API should be broken up into a heirarchy of types
|
||||
in a language binding.
|
||||
</para>
|
||||
<programlisting>
|
||||
cairo_surface_t
|
||||
cairo_image_surface_t
|
||||
cairo_atsui_surface_t
|
||||
cairo_win32_surface_t
|
||||
cairo_xlib_surface_t
|
||||
</programlisting>
|
||||
<para>
|
||||
Unlike patterns, the constructors and methods on these types are
|
||||
clearly named, and can be trivially associated with the
|
||||
appropriate subtype. Many language bindings will want to avoid
|
||||
binding the platform-specific subtypes at all, since the
|
||||
methods on these types are not useful without passing in native
|
||||
C types. Unless there is a language binding for Xlib available,
|
||||
there is no way to represent a XLib <type>Display</type> * in
|
||||
that language.
|
||||
</para>
|
||||
<para>
|
||||
This doesn't mean that platform-specific surface types can't
|
||||
be used in a language binding that doesn't bind the constructor.
|
||||
A very common situation is to use a cairo language binding in
|
||||
combination with a binding for a higher level system like
|
||||
the <ulink url="http://www.gtk.org/">GTK+</ulink> widget
|
||||
toolkit. In such a situation, the higher level toolkit provides
|
||||
ways to get references to platform specific surfaces.
|
||||
</para>
|
||||
<para>
|
||||
The <link
|
||||
linkend="cairo-surface-set-user-data"><function>cairo_surface_set_user_data()</function></link>,
|
||||
and <link
|
||||
linkend="cairo-surface-get-user-data"><function>cairo_surface_get_user_data()</function></link>
|
||||
methods are provided for use in language bindings, and should
|
||||
not be directly exposed to applications. One example of the use
|
||||
of these functions in a language binding is creating a binding for:
|
||||
</para>
|
||||
<programlisting>
|
||||
cairo_surface_t *
|
||||
<link linkend="cairo-image-surface-create-for-data"><function>cairo_image_surface_create_for_data</function></link> (unsigned char *data,
|
||||
cairo_format_t format,
|
||||
int width,
|
||||
int height,
|
||||
int stride);
|
||||
</programlisting>
|
||||
<para>
|
||||
The memory block passed in for <parameter>data</parameter> must be
|
||||
kept around until the surface is destroyed, so the language
|
||||
binding must have some way of determining when that happens. The
|
||||
way to do this is to use the <parameter>destroy</parameter>
|
||||
argument to <function>cairo_surface_set_user_data()</function>.
|
||||
</para>
|
||||
<remark>
|
||||
Some languages may not have a suitable “pointer to a block of
|
||||
data” type to pass in for <parameter>data</parameter>. And even
|
||||
where a language does have such a type, the user will be
|
||||
frequently able to cause the backing store to be reallocated
|
||||
to a different location or truncated. Should we recommend a
|
||||
standard type name and binding for a buffer object here?
|
||||
</remark>
|
||||
</sect1>
|
||||
<sect1 id="bindings-fonts">
|
||||
<title>Fonts</title>
|
||||
<para>
|
||||
Fonts are once more an area where there is a hierarchy of types:
|
||||
</para>
|
||||
<programlisting>
|
||||
cairo_font_face_t
|
||||
cairo_ft_font_face_t
|
||||
cairo_win32_font_face_t
|
||||
cairo_scaled_font_t
|
||||
cairo_ft_scaled_font_t
|
||||
cairo_win32_scaled_font_t
|
||||
</programlisting>
|
||||
<para>
|
||||
The methods on the subtypes are, however, not useful without
|
||||
bindings for fontconfig and FreeType or for the Win32 GDI,
|
||||
so most language bindings will choose not to bind these
|
||||
types.
|
||||
</para>
|
||||
<para>
|
||||
The <link
|
||||
linkend="cairo-font-face-set-user-data"><function>cairo_font_face_set_user_data()</function></link>,
|
||||
and <link
|
||||
linkend="cairo-font-face-get-user-data"><function>cairo_font_face_get_user_data()</function></link>
|
||||
methods are provided for use in language bindings, and should
|
||||
not be directly exposed to applications.
|
||||
</para>
|
||||
</sect1>
|
||||
<sect1 id="bindings-path">
|
||||
<title>cairo_path_t</title>
|
||||
<para>
|
||||
The <link linkend="cairo-path-t"><type>cairo_path_t</type></link> type is one
|
||||
area in which most language bindings will differ significantly
|
||||
from the C API. The C API for <type>cairo_path_t</type> is
|
||||
designed for efficiency and to avoid auxiliary objects that
|
||||
would be have to be manually memory managed by the
|
||||
application. However,
|
||||
a language binding should not present <type>cairo_path_t</type> as an
|
||||
array, but rather as an opaque that can be iterated
|
||||
over. Different languages have quite different conventions for
|
||||
how iterators work, so it is impossible to give an exact
|
||||
specification for how this API should work, but the type names
|
||||
and methods should be similar to the language's mapping of the following:
|
||||
</para>
|
||||
<programlisting>
|
||||
typedef struct cairo_path_iterator cairo_path_iterator_t;
|
||||
typedef struct cairo_path_element cairo_path_element_t;
|
||||
|
||||
cairo_path_iterator_t *
|
||||
cairo_path_get_iterator (cairo_path_t *path);
|
||||
|
||||
cairo_bool_t
|
||||
cairo_path_iterator_has_next (cairo_path_iterator_t *iterator);
|
||||
|
||||
cairo_path_element_t *
|
||||
cairo_path_iterator_next (cairo_path_iterator_t *iterator);
|
||||
|
||||
cairo_path_element_type_t
|
||||
cairo_path_element_get_type (cairo_path_element_t *element);
|
||||
|
||||
void
|
||||
cairo_path_element_get_point (cairo_path_element_t *element,
|
||||
int index,
|
||||
double *x,
|
||||
double *y);
|
||||
</programlisting>
|
||||
<para>
|
||||
The above is written using the Java conventions for
|
||||
iterators. To illustrate how the API for PathIterator might
|
||||
depend on the native iteration conventions of the API, examine
|
||||
three versions of the loop, first written in a hypothetical Java
|
||||
binding:
|
||||
</para>
|
||||
<programlisting>
|
||||
PathIterator iter = cr.copyPath().iterator();
|
||||
while (cr.hasNext()) {
|
||||
PathElement element = iter.next();
|
||||
if (element.getType() == PathElementType.MOVE_TO) {
|
||||
Point p = element.getPoint(0);
|
||||
doMoveTo (p.x, p.y);
|
||||
}
|
||||
}</programlisting>
|
||||
<para>
|
||||
And then in a hypothetical C++ binding:
|
||||
</para>
|
||||
<programlisting>
|
||||
Path path = cr.copyPath();
|
||||
for (PathIterator iter = path.begin(); iter != path.end(); iter++) {
|
||||
PathElement element = *iter;
|
||||
if (element.getType() == PathElementType.MOVE_TO) {
|
||||
Point p = element.getPoint(0);
|
||||
doMoveTo (p.x, p.y);
|
||||
}
|
||||
}</programlisting>
|
||||
<para>
|
||||
And then finally in a Python binding:
|
||||
</para>
|
||||
<programlisting>
|
||||
for element in cr.copy_path():
|
||||
if element.getType == cairo.PATH_ELEMENT_MOVE_TO:
|
||||
(x, y) = element.getPoint(0)
|
||||
doMoveTo (x, y);</programlisting>
|
||||
<para>
|
||||
While many of the API elements stay the same in the three
|
||||
examples, the exact iteration mechanism is quite different, to
|
||||
match how users of the language would expect to iterate over
|
||||
a container.
|
||||
</para>
|
||||
<para>
|
||||
You should not present an API for mutating or for creating new
|
||||
<type>cairo_path_t</type> objects. In the future, these
|
||||
guidelines may be extended to present an API for creating a
|
||||
<type>cairo_path_t</type> from scratch for use with
|
||||
<link
|
||||
linkend="cairo-append-path"><function>cairo_append_path()</function></link>
|
||||
but the current expectation is that <function>cairo_append_path()</function> will
|
||||
mostly be used with paths from
|
||||
<link
|
||||
linkend="cairo-append-path"><function>cairo_copy_path()</function></link>.
|
||||
</para>
|
||||
</sect1>
|
||||
</appendix>
|
||||
<!--
|
||||
Local variables:
|
||||
mode: sgml
|
||||
sgml-parent-document: ("cairo-docs.xml" "book" "book" "appendix")
|
||||
End:
|
||||
-->
|
||||
Loading…
Add table
Reference in a new issue