📄 language-bindings.xml
字号:
<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_RESTORECAIRO_STATUS_INVALID_POP_GROUPCAIRO_STATUS_NO_CURRENT_POINTCAIRO_STATUS_INVALID_MATRIXCAIRO_STATUS_NO_TARGET_SURFACECAIRO_STATUS_INVALID_STRINGCAIRO_STATUS_SURFACE_FINISHEDCAIRO_STATUS_BAD_NESTING/* Language binding implementation */CAIRO_STATUS_NULL_POINTERCAIRO_STATUS_INVALID_PATH_DATACAIRO_STATUS_SURFACE_TYPE_MISMATCH/* Other */ CAIRO_STATUS_READ_ERRORCAIRO_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 hierarchy 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 cairo_beos_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_tcairo_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_tcairo_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_tcairo_path_element_get_type (cairo_path_element_t *element); voidcairo_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: sgmlsgml-parent-document: ("cairo-docs.xml" "book" "book" "appendix")End:-->
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -