by Michael Urman
Cairo is a powerful 2d graphics library. This document introduces you to how cairo works and many of the functions you will use to create the graphic experience you desire.
Cairo 是一个功能强大的2D图形库。本文为您介绍cairo是如何工作的,以及许多您在体验时创建图形所用到的函数。
In order to follow along on your computer, you need the following things:
为了在您的计算机上继续以下的过程,你需要:
Alternately, if you're up for a challenge, you can translate the examples to your preferred language and host environment and only need cairo from above.
除此以外, 如果您愿意面对挑战,您可以将例子翻译为您喜欢的编程语言,并且主机环境只 需要上面的cairo。
Note: All the example code has a dependency on cairo 1.2.0 or higher for cairo.SVGSurface. In addition several examples need push_group() and pop_group(). In a pinch you can work around the first by changing instances of cairo.SVGSurface(filename + '.svg', width, height) to cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height), but really you should consider upgrading. I may try to remove as much of this dependency as possible in a future revision, but don't count on it: I run 1.2 myself.
注意: 所有 cairo.SVGSurface 例子代码需要 cairo 1.2.0 或更高版本。其他几个例子需要 push_group() 和 pop_group(). 必要时,您可以 解决第一个问题,通过将 cairo.SVGSurface(filename + '.svg', width, height) 改变为 cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height), 但是,您真正应该考虑的时升级,我可以在将来的版本中尽量去删除这些依赖,但 不要指望这个,我自己使用的是1.2。
In order to explain the operations used by cairo, we first delve into a model of how cairo models drawing. There are only a few concepts involved, which are then applied over and over by the different methods. First I'll describe the nouns: destination, source, mask, path, and context. After that I'll describe the verbs which offer ways to manipulate the nouns and draw the graphics you wish to create. And don't read this now, but here's the code behind all the diagrams.
为了解释cairo使用的操作, 我们首先考察一下cairo建立的绘图模型, 这个模型一次又一次地被不同的方法使用,这儿只有几个概念。首先,我将描述: nouns: destination, source, mask, path, and context. 然后我将描述: verbs 它提供了操作nouns的方法,以及绘制你想要创建的图形的方法。这是所有图形后面的 code ,但现在不要阅读它。
Cairo's nouns are somewhat abstract. To make them concrete I'm including diagrams that depict how they interact. The first three nouns are the three layers in the diagrams you see in this section. The fourth noun, the path, is drawn on the middle layer when it is relevant. The final noun, the context, isn't shown.
Cairo 的 nouns多少有点抽象,为了使其具体,我使用图形来描述Nouns之间的相互关系。前三个nouns是你在这部分看到的图形的三个层。第四个noun,path是is drawn on the middle layer when it is relevant. 最后一个noun,context它不显示。
The destination is the surface on which you're drawing. It may be tied to an array of pixels like in these PyGTK tutorials, or it might be tied to a SVG or PDF file, or something else. This surface collects the elements of your graphic as you apply them, allowing you to build up a complex work as though painting on a canvas.
destination 是您正在绘制的surface表面 它也许和一组象素相关,就像PyGTK教程中一样,或者和SVG 或 PDF 文件相关,或者其他。这个表面集合你的图形的元素,就像你贴上去的一样,它允许你构建复杂工作,就像在画布上绘图一样。
The source is the "paint" you're about to work with. I show this as it is—plain black for several examples—but translucent to show lower layers. Unlike real paint, it doesn't have to be a single color; it can be a pattern or even a previously created destination surface. Also unlike real paint it can contain transparency information—the Alpha channel.
source是你要使用的“颜料”,我显示这些就像它们是颜料一样,在接下的几个例子中用黑色表示,但半透明用来表示较低的层。不像真正的颜料,它们不必是单色的,它们可以是 pattern模式 或者是前面创建的destination 的surface 表面. 也不像真正的颜料,它们可以包含透明信息-如Alpha channel
The mask is the most important piece: it controls where you apply the source to the destination. I will show it as a yellow layer with holes where it lets the source through. When you apply a drawing verb, it's like you stamp the source to the destination. Anywhere the mask allows, the source is copied. Anywhere the mask disallows, nothing happens.
屏蔽是最重要的部分:它控制这你将源贴到目标的什么位置。我将用黄色层表示它,上面的洞让源通过。当你使用绘图动词时,它就像你把源贴在目标上。任何屏蔽允许的地方,源被拷贝,任何屏蔽禁止的地方,什么也不会发生。
The path is somewhere between part of the mask and part of the context. I will show it as thin green lines on the mask layer. It is manipulated by path verbs, then used by drawing verbs.
路径是位于mask部分和context部分中间的地方。我用细绿线在屏蔽层上表示它。它由路径动词verbs操作然后由绘图动词verbs使用。
The context keeps track of everything that verbs affect. It tracks one source, one destination, and one mask. It also tracks several helper variables like your line width and style, your font face and size, and more. Most importantly it tracks the path, which is turned into a mask by drawing verbs.
context上下文跟踪动词影响的一切。它跟踪一个source,一个destination,和一个mask。它还跟踪几个辅助变量,如你的线宽和线型,你的字体和大小,以及其他。最重要的是它跟踪路径,通过绘图verb把它转换成mask。
The reason you are using cairo in a program is to draw. Cairo internally draws with one fundamental drawing operation: the source and mask are freely placed somewhere over the destination. Then the layers are all pressed together and the paint from the source is transferred to the destination wherever the mask allows it. To that extent the following five drawing verbs, or operations, are all similar. They differ by how they construct the mask.
你在程序中使用cairo的原因是绘图。 Cairo内部使用一个基本绘图操作进行绘制。 source 和 mask 被自由地放置在 destination上面的某些地方. 然后这些层被压在一起,并且在任何mask 允许的地方,颜料从source被传送到destination ,于是,扩展出下面五个绘图verbs,或者说操作operations,它们都相似,它们的区别在于它们构造的mask不同。
The stroke() operation takes a virtual pen along the path. It allows the source to transfer through the mask in a thin (or thick) line around the path, according to the pen's line width, dash style, and line caps.
Cairo Tutorial: Diagrams (Section #stroke) cr.set_line_width(0.1) cr.set_source_rgb(0, 0, 0) cr.rectangle(0.25, 0.25, 0.5, 0.5) cr.stroke()
stroke() 操作使用一个虚笔绘制 path. 它允许在一个细线(或粗线)围绕的path范围的 source 通过 mask 传递, 依据笔的, line线宽 width, dash style线型和 line caps接头。
Cairo Tutorial: Diagrams (Section #stroke) cr.set_line_width(0.1) cr.set_source_rgb(0, 0, 0) cr.rectangle(0.25, 0.25, 0.5, 0.5) cr.stroke()
The fill() operation instead uses the path like the lines of a coloring book, and allows the source through the mask within the hole whose boundaries are the path. For complex paths (paths with multiple closed sub-paths—like a donut—or paths that self-intersect) this is influenced by the fill rule. Note that while stroking the path transfers the source for half of the line width on each side of the path, filling a path fills directly up to the edge of the path and no further.
Cairo Tutorial: Diagrams (Section #fill) cr.set_source_rgb(0, 0, 0) cr.rectangle(0.25, 0.25, 0.5, 0.5) cr.fill()
fill() 操作不仅仅使用path , 并允许 在路径边界范围包围的孔洞内的source 通过 mask 。对于复杂的路径( multiple closed 路径, sub-paths—like a donut—or paths that self-intersect)则由 fill rule 规则影响. 如:当给路径描边时,传递的源是路径每个边的线宽的一半。填充路径时,填充到达路径的边界并且无扩大。
Cairo Tutorial: Diagrams (Section #fill) cr.set_source_rgb(0, 0, 0) cr.rectangle(0.25, 0.25, 0.5, 0.5) cr.fill()
The show_text() operation forms the mask from text. It may be easier to think of show_text() as a shortcut for creating a path with text_path() and then using fill() to transfer it. Be aware show_text() caches glyphs so is much more efficient if you work with a lot of text.
Cairo Tutorial: Diagrams (Section #text) cr.set_source_rgb(0.0, 0.0, 0.0) cr.select_font_face("Georgia", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) cr.set_font_size(1.2) x_bearing, y_bearing, width, height = cr.text_extents("a")[:4] cr.move_to(0.5 - width / 2 - x_bearing, 0.5 - height / 2 - y_bearing) cr.show_text("a")
show_text() 操作从text形成mask。它也许很容易认为 operation forms the mask from text. It may be easier to think of show_text() 是用 text_path() 创建创建路径的简化,然后使用 fill() 操作传递. 注意 show_text() 缓冲图形,所以,在你使用很多文本时它更高效。
Cairo Tutorial: Diagrams (Section #text) cr.set_source_rgb(0.0, 0.0, 0.0) cr.select_font_face("Georgia", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) cr.set_font_size(1.2) x_bearing, y_bearing, width, height = cr.text_extents("a")[:4] cr.move_to(0.5 - width / 2 - x_bearing, 0.5 - height / 2 - y_bearing) cr.show_text("a")
The paint() operation uses a mask that transfers the entire source to the destination. Some people consider this an infinitely large mask, and others consider it no mask; the result is the same. The related operation paint_with_alpha() similarly allows transfer of the full source to destination, but it transfers only the provided percentage of the color.
Cairo Tutorial: Diagrams (Section #paint) cr.set_source_rgb(0.0, 0.0, 0.0) cr.paint_with_alpha(0.5)
paint() 操作使用mask 传输整个 source 到 destination . 有的人认为这是一个无限大的mask Some people consider this an infinitely large mask, 但也有的人认为是没有mask,结果是一样的。相关操作 paint_with_alpha() 类似于允许传递全部的source 到 destination, 但它只传递制定百分比的颜色。
Cairo Tutorial: Diagrams (Section #paint) cr.set_source_rgb(0.0, 0.0, 0.0) cr.paint_with_alpha(0.5)
The mask() and mask_surface() operations allow transfer according to the transparency/opacity of a second source pattern or surface. Where the pattern or surface is opaque, the current source is transferred to the destination. Where the pattern or surface is transparent, nothing is transferred.
Cairo Tutorial: Diagrams (Section #mask) self.linear = cairo.LinearGradient(0, 0, 1, 1) self.linear.add_color_stop_rgb(0, 0, 0.3, 0.8) self.linear.add_color_stop_rgb(1, 0, 0.8, 0.3) self.radial = cairo.RadialGradient(0.5, 0.5, 0.25, 0.5, 0.5, 0.5) self.radial.add_color_stop_rgba(0, 0, 0, 0, 1) self.radial.add_color_stop_rgba(0.5, 0, 0, 0, 0) cr.set_source(self.linear) cr.mask(self.radial)
mask() 和 mask_surface() 操作允许依据第二个源图案或表面的透明/不透明情况进行传递。在图案或表明不透明的地方,当前源被传递到目标。在图案或表面透明的地方,什么也不传递。
Cairo Tutorial: Diagrams (Section #mask) self.linear = cairo.LinearGradient(0, 0, 1, 1) self.linear.add_color_stop_rgb(0, 0, 0.3, 0.8) self.linear.add_color_stop_rgb(1, 0, 0.8, 0.3) self.radial = cairo.RadialGradient(0.5, 0.5, 0.25, 0.5, 0.5, 0.5) self.radial.add_color_stop_rgba(0, 0, 0, 0, 1) self.radial.add_color_stop_rgba(0.5, 0, 0, 0, 0) cr.set_source(self.linear) cr.mask(self.radial)
In order to create an image you desire, you have to prepare the context for each of the drawing verbs. To use stroke() or fill() you first need a path. To use show_text() you must position your text by its insertion point. To use mask() you need a second source pattern or surface. And to use any of the operations, including paint(), you need a primary source.
为了能够创建你想要的图形,你必须为每个绘制动词verbs准备 context上下文 为了使用stroke() 或者 fill() 你首先需要一个路径path 为了使用show_text() 你必须通过插入点定位你的文本。为了使用mask() 你需要第二个源pattern图案 或 surface表面. 并且,为了使用任何包括 paint(), 的操作,你需要一个起始源.
There are three main kinds of sources in cairo: colors, gradients, and images. Colors are the simplest; they use a uniform hue and opacity for the entire source. You can select these without any preparation with set_source_rgb() and set_source_rgba(). Using set_source_rgb(r, g, b) is equivalent to using set_source_rgba(r, g, b, 1.0), and it sets your source color to use full opacity.
Cairo Tutorial: Drawing (Section #rgba) cr.set_source_rgb(0, 0, 0) cr.move_to(0, 0) cr.line_to(1, 1) cr.move_to(1, 0) cr.line_to(0, 1) cr.set_line_width(0.2) cr.stroke() cr.rectangle(0, 0, 0.5, 0.5) cr.set_source_rgba(1, 0, 0, 0.80) cr.fill() cr.rectangle(0, 0.5, 0.5, 0.5) cr.set_source_rgba(0, 1, 0, 0.60) cr.fill() cr.rectangle(0.5, 0, 0.5, 0.5) cr.set_source_rgba(0, 0, 1, 0.40) cr.fill()
在cairo中有三种主要的源sources:颜色colors, gradients梯度, 和images图形. 颜色是最简单的;对于整个源它们使用统一的色彩和不透明度。你可以通过 You can select these without any preparation with set_source_rgb() 和 set_source_rgba() . 选择它们而不需要任何准备工作。使用 set_source_rgb(r, g, b) 和使用 set_source_rgba(r, g, b, 1.0), 相同,set_source_rgb将你的源的颜色设置为完全不透明。
Cairo Tutorial: Drawing (Section #rgba) cr.set_source_rgb(0, 0, 0) cr.move_to(0, 0) cr.line_to(1, 1) cr.move_to(1, 0) cr.line_to(0, 1) cr.set_line_width(0.2) cr.stroke() cr.rectangle(0, 0, 0.5, 0.5) cr.set_source_rgba(1, 0, 0, 0.80) cr.fill() cr.rectangle(0, 0.5, 0.5, 0.5) cr.set_source_rgba(0, 1, 0, 0.60) cr.fill() cr.rectangle(0.5, 0, 0.5, 0.5) cr.set_source_rgba(0, 0, 1, 0.40) cr.fill()
Gradients describe a progression of colors by setting a start and stop reference location and a series of "stops" along the way. Linear gradients are built from two points which pass through parallel lines to define the start and stop locations. Radial gradients are also built from two points, but each has an associated radius of the circle on which to define the start and stop locations. Stops are added to the gradient with add_color_stop_rgb() and add_color_stop_rgba() which take a color like set_source_rgb*(), as well as an offset to indicate where it lies between the reference locations. The colors between adjacent stops are averaged over space to form a smooth blend. Finally, the behavior beyond the reference locations can be controlled with set_extend(). (TODO)
Cairo Tutorial: Drawing (Section #gradient) radial = cairo.RadialGradient(0.25, 0.25, 0.1, 0.5, 0.5, 0.5) radial.add_color_stop_rgb(0, 1.0, 0.8, 0.8) radial.add_color_stop_rgb(1, 0.9, 0.0, 0.0) for i in range(1, 10): for j in range(1, 10): cr.rectangle(i/10.0 - 0.04, j/10.0 - 0.04, 0.08, 0.08) cr.set_source(radial) cr.fill() linear = cairo.LinearGradient(0.25, 0.35, 0.75, 0.65) linear.add_color_stop_rgba(0.00, 1, 1, 1, 0) linear.add_color_stop_rgba(0.25, 0, 1, 0, 0.5) linear.add_color_stop_rgba(0.50, 1, 1, 1, 0) linear.add_color_stop_rgba(0.75, 0, 0, 1, 0.5) linear.add_color_stop_rgba(1.00, 1, 1, 1, 0) cr.rectangle(0.0, 0.0, 1, 1) cr.set_source(linear) cr.fill()
梯度是指通过设置起始和结束的指定位置以及沿着路径的一系列"stops"(降落点)实现的颜色层级。 Linear gradients 线性梯度 从两个点产生,这两 are built from two points which pass through parallel lines to define the start and stop locations. Radial gradients径向梯度也从两个点产生,但每个点 are also built from two points, but each has an associated radius of the circle on which to define the start and stop locations. Stops are added to the gradient with add_color_stop_rgb() and add_color_stop_rgba() which take a color like set_source_rgb*(), as well as an offset to indicate where it lies between the reference locations. The colors between adjacent stops are averaged over space to form a smooth blend. Finally, the behavior beyond the reference locations can be controlled with set_extend(). (TODO)
Cairo Tutorial: Drawing (Section #gradient) radial = cairo.RadialGradient(0.25, 0.25, 0.1, 0.5, 0.5, 0.5) radial.add_color_stop_rgb(0, 1.0, 0.8, 0.8) radial.add_color_stop_rgb(1, 0.9, 0.0, 0.0) for i in range(1, 10): for j in range(1, 10): cr.rectangle(i/10.0 - 0.04, j/10.0 - 0.04, 0.08, 0.08) cr.set_source(radial) cr.fill() linear = cairo.LinearGradient(0.25, 0.35, 0.75, 0.65) linear.add_color_stop_rgba(0.00, 1, 1, 1, 0) linear.add_color_stop_rgba(0.25, 0, 1, 0, 0.5) linear.add_color_stop_rgba(0.50, 1, 1, 1, 0) linear.add_color_stop_rgba(0.75, 0, 0, 1, 0.5) linear.add_color_stop_rgba(1.00, 1, 1, 1, 0) cr.rectangle(0.0, 0.0, 1, 1) cr.set_source(linear) cr.fill()
Images include both surfaces loaded from existing files with cairo.ImageSurface.create_from_png() and surfaces created from within cairo as an earlier destination. As of cairo 1.2, the easiest way to make and use an earlier destination as a source is with push_group() and either pop_group() or pop_group_to_source(). Use pop_group_to_source() to use it just until you select a new source, and pop_group() when you want to save it so you can select it over and over again with set_source().
图形包括使用cairo.ImageSurface.create_from_png() 函数从存在的文件加载的表面和在cairo内从前面的目标创建的表面。如cairo 1.2, 最容易的从一个源生成和使用destination的办法是用函数 push_group() 和或 pop_group() 或 pop_group_to_source(). 只有当你用选择了一个新的源后,你才可以用函数 pop_group_to_source() 使用源,并且 pop_group() when you want to save it so you can select it over and over again with set_source().
Cairo always has an active path. If you call stroke() it will draw the path with your line settings. If you call fill() it will fill the inside of the path. But as often as not, the path is empty, and both calls will result in no change to your destination. Why is it empty so often? For one, it starts that way; but more importantly after each stroke() or fill() it is emptied again to let you start building your next path.
What if you want to do multiple things with the same path? For instance to draw a red rectangle with a black border, you would want to fill the rectangle path with a red source, then stroke the same path with a black source. A rectangle path is easy to create multiple times, but a lot of paths are more complex.
Cairo supports easily reusing paths by having alternate versions of its operations. Both draw the same thing, but the alternate doesn't reset the path. For stroking, alongside stroke() there is stroke_preserve(); for filling, fill_preserve() joins fill(). Even setting the clip has a preserve variant. Apart from choosing when to preserve your path, there are only a couple common operations.
Cairo 总是具有一个活动路径,如果你调用stroke() ,它会使用你的线设置绘制路径。如果你调用 fill() ,它会填充路径内部。但经常不这样,因为路径是空的,这两个调用的结果都不会改变你的 destination。为什么它经常是空的呢? 一个原因是,它开始就是这样的,但更重要的是在每个stroke() 或 fill() 调用后,它又一次变为空路径,以便让你构造你的下个路径。
要是你想用同一个路径做多件事情W又该怎么办呢? 比如,绘制一个带黑边的红色矩形,你会想使用红色的源填充举行,然后用黑色的源stroke同一个路径矩形路径容易被创建多次,但很多路径就会便得很复杂。
Cairo通过操作另一个版本的支持方便地重用路径。都是绘制同一个个东西,但另一个版本不重置路径。 对于stroke() ,对应的是 stroke_preserve(); 对应于填充fill()的是fill_preserve() 。就连clip也有保存参数的版本. 除了打算保存你的路径选择的这几个操作之外,还有几个一般的操作。
Cairo uses a connect-the-dots style system when creating paths. Start at 1, draw a line to 2, then 3, and so forth. When you start a path, or when you need to start a new sub-path, you want it to be like point 1: it has nothing connecting to it. For this, use move_to(). This sets the current reference point without making the path connect the previous point to it. There is also a relative coordinate variant, rel_move_to(), which sets the new reference a specified distance away from the current reference instead. After setting your first reference point, use the other path operations which both update the reference point and connect to it in some way.
Cairo Tutorial: Drawing (Section #moveto) cr.move_to(0.25, 0.25)
Cairo在创建路径使用 connect-the-dots(连线)样式机制. 开始在1,然后连线到2,然后是3,然后是4。当你开始一个路径时,或者当你需要开始一个子路径时,你想让它就象point 1: 没有和它相连的。对于这种情况,使用 move_to() 。这个函数设置路径的参考点,而不使路径连接到它的前一个点上。它也有一个向多坐标版本rel_move_to() ,它通过相对当前参考点的位移设置新的参考点。在设置第一个参考点后,以相同的方式,使用其他路径更新参考点和连接操作设置新的参考点。
Cairo Tutorial: Drawing (Section #moveto) cr.move_to(0.25, 0.25)
Whether with absolute coordinates line_to() (extend the path from the reference to this point), or relative coordinates rel_line_to() (extend the path from the reference this far in this direction), the path connection will be a straight line. The new reference point will be at the other end of the line.
Cairo Tutorial: Drawing (Section #lineto) cr.line_to(0.5, 0.375) cr.rel_line_to(0.25, -0.125)
无论使用绝对坐标line_to() (从参考点延伸路径到这个点extend the path from the reference to this point),或相对坐标 rel_line_to() (沿着这个方向从参考点延伸路径extend the path from the reference this far in this direction),路径连接都将是直线,新参考点将是线的另一个端点。
Cairo Tutorial: Drawing (Section #lineto) cr.line_to(0.5, 0.375) cr.rel_line_to(0.25, -0.125)
Arcs are parts of the outside of a circle. Unlike straight lines, the point you directly specify is not on the path. Instead it is the center of the circle that makes up the addition to the path. Both a starting and ending point on the circle must be specified, and these points are connected either clockwise by arc() or counter-clockwise by arc_negative(). If the previous reference point is not on this new curve, a straight line is added from it to where the arc begins. The reference point is then updated to where the arc ends. There are only absolute versions.
Cairo Tutorial: Drawing (Section #arc) cr.arc(0.5, 0.5, 0.25 * sqrt(2), -0.25 * pi, 0.25 * pi)
弧是圆的外边的一部分,与直线不同,你直接指定的点不在路径上,而是圆心,用来标识路径。 the addition to the path. 圆的起始点和结束点必须指定,并且这两个点或者用arc()顺时针连接或者用arc_negative() 逆时针连接。如果前一个参考点不是在新的曲线上,在弧的开始处和前一个参考点之间添加一条直线。然后参考点更新为弧的端点。这是绝对位置版本。
Cairo Tutorial: Drawing (Section #arc) cr.arc(0.5, 0.5, 0.25 * sqrt(2), -0.25 * pi, 0.25 * pi)
Curves in cairo are cubic Bézier splines. They start at the current reference point and smoothly follow the direction of two other points (without going through them) to get to a third specified point. Like lines, there are both absolute (curve_to()) and relative (rel_curve_to()) versions. Note that the relative variant specifies all points relative to the previous reference point, rather than each relative to the preceding control point of the curve.
Cairo Tutorial: Drawing (Section #curveto) cr.rel_curve_to(-0.25, -0.125, -0.25, 0.125, -0.5, 0)
cairo中的曲线是三次Bézier样条。它从当前参考点开始,并平滑地沿着另两个点(不通过这两个点)的方向到达第三个点。象直线一样,它们都是绝对坐标curve_to()) 和相对坐标 (rel_curve_to() ) 版本。注意相对坐标版本指定所有的点相对于前一个参考点,而不是相对每个的点的前面曲线控制点。
Cairo Tutorial: Drawing (Section #curveto) cr.rel_curve_to(-0.25, -0.125, -0.25, 0.125, -0.5, 0)
Cairo can also close the path by drawing a straight line to the beginning of the current sub-path. This straight line can be useful for the last edge of a polygon, but is not directly useful for curve-based shapes. A closed path is fundamentally different from an open path: it's one continuous path and has no start or end. A closed path has no line caps for there is no place to put them.
Cairo Tutorial: Drawing (Section #closepath) cr.close_path()
Cairo也能通过画一条直线到当前子路径的起点来闭合路径。这条直线对多边形的最后一条边很有用,但对于曲线类的图形不是直接有用。闭合路径和开发路径的根本区别是:闭合路径是没有开始或结束的连续路径。闭合路径没有line caps(线头),因为没有地方放它。
Cairo Tutorial: Drawing (Section #closepath) cr.close_path()
Finally text can be turned into a path with text_path(). Paths created from text are like any other path, supporting stroke or fill operations. This path is placed anchored to the current reference point, so move_to() your desired location before turning text into a path. However there are performance concerns to doing this if you are working with a lot of text; when possible you should prefer using the verb show_text() over text_path() and fill().
文本最后用text_path() 转换为路径。从文本创建路径和从其他路径创建路径一样,支持stroke 或 fill操作。这个路径被放置在当前参考点,所以在将文本转换为路径前, This path is placed anchored to the current reference point, so move_to() 到你想要的位置。然而,如果你使用许多文本,这样做会有性能问题。如果可能,除text_path() 和 fill() 之外尽量使用verb show_text() 。
To use text effectively you need to know where it will go. The methods font_extents() and text_extents() get you this information. Since this diagram is hard to see so small, I suggest getting its source and bumping the size up to 600. It shows the relation between the reference point (red dot); suggested next reference point (blue dot); bounding box (dashed blue lines); bearing displacement (solid blue line); and height, ascent, baseline, and descent lines (dashed green).
为有效使用文本,你需要知道它的来龙去脉。 To use text effectively you need to know where it will go. 方法 font_extents() 和 text_extents() 为你获取这些信息. 由于这个图太小很难看明白,我建议 获取它的 source 并将尺寸扩大到600。它显示了这些点相互关系,参考点(红色点);挨着参考点的提示点(蓝色点),边界(蓝色点划线);相对位移(蓝色实线); reference point (red dot); suggested next reference point (blue dot); bounding box (dashed blue lines); bearing displacement (solid blue line); and height, ascent, baseline, and descent lines (dashed green).
The reference point is always on the baseline. The descent line is below that, and reflects a rough bounding box for all characters in the font. However it is an artistic choice intended to indicate alignment rather than a true bounding box. The same is true for the ascent line above. Next above that is the height line, the artist-recommended spacing between subsequent baselines. All three of these are reported as distances from the baseline, and expected to be positive despite their differing directions.
参考点总在baseline上,descent line在baseline下边,反映出字体中所有字符的大致边界。然而,它是为了指示对齐的技术上的选择,而不是真实的边界。上面的ascent line也是如此。下一个是height line高度线,它是一前一后两个baseline之间的技术上推荐的距离。所有这三条线所说的距离都是基于baseline的,尽管它们的方向不同,但都要求正值。
The bearing is the displacement from the reference point to the upper-left corner of the bounding box. It is often zero or a small positive value for x displacement, but can be negative x for characters like j as shown; it's almost always a negative value for y displacement. The width and height then describe the size of the bounding box. The advance takes you to the suggested reference point for the next letter. Note that bounding boxes for subsequent blocks of text can overlap if the bearing is negative, or the advance is smaller than the width would suggest.
相对位置是参考点到边界左上角的位移。x的位移通常是0或者小的正数,但对于象上面显示的字符j ,x可以是负数。y的位移总是负数。width 和 height 指示边框的大小。advance为你指示下一个字符的参考点。注意:如果相对位移为负值,或者advance比下一个参考点的宽度小,text的相邻块可以相互重叠,
In addition to placement, you also need to specify a face, style, and size. Set the face and style together with select_font_face(), and the size with set_font_size(). If you need even finer control, try getting a cairo.FontOptions() with get_font_options(), tweaking it, and setting it with set_font_options().
除了位移,你还需指定面,风格和尺寸。用 select_font_face() 函数设置面和风格,用 and the size with set_font_size() 函数设置尺寸。如果你还想更好的控制,用 get_font_options() 函数获取 cairo.FontOptions() 。 修改之后,用 set_font_options() 函数设置。
When working in GTK+, there is also the pangocairo.CairoContext. I think using cairo's builtin font functionality is more suitable for handling text with picky size restrictions and complex positioning, whereas pangocairo.CairoContext may more suitable when you're trying to create widget text that looks like other GTK+ widget text.
在GTK+, 也有一个函数 pangocairo.CairoContext 。我认为使用cairo内置的字体功能,对于处理text的尺寸限制和复杂定位更合适。但当你想要创建那种看上去象其他 GTK+ 文本部件的文本部件时,pangocairo.CairoContext 也许更适合
Transforms have three major uses. First they allow you to set up a coordinate system that's easy to think in and work in, yet have the output be of any size. Second they allow you to make helper functions that work at or around a (0, 0) but can be applied anywhere in the output image. Thirdly they let you deform the image, turning a circular arc into an elliptical arc, etc. Transforms are a way of setting up a relation between two coordinate systems. The device-space coordinate system is tied to the surface, and cannot change. The user-space coordinate system matches that space by default, but can be changed for the above reasons. The helper functions user_to_device() and user_to_device_distance() tell you what the device-coordinates are for a user-coordinates position or distance. Correspondingly device_to_user() and device_to_user_distance() tell you user-coordinates for a device-coordinates position or distance. Remember to send positions through the non-distance variant, and relative moves or other distances through the distance variant.
变换有三个主要的用途。首先它们允许你设置容易思考和容易工作的坐标系统,还有任意尺寸输出。第二是它们允许你定制工作在(0, 0)点或(0, 0) 附近的辅助功能,并且能被应用到输出图象的任何位置。第三它们让你改变图形,如将圆弧变为椭圆弧等。变换是在两个坐标系统之间建立联系的方法,设备空间坐标和表面相联系,不能改变。用户空间坐标缺省与这个空间相匹配。但因为以上的原因,可以改变。辅助函数 user_to_device() 和 user_to_device_distance() 告诉你对于用户坐标的位置或者距离,对应的设备坐标是什么。同样 device_to_user() 和 device_to_user_distance() 告诉对于设备坐标的位置和距离,对应的用户坐标是什么。注意:通过非距离变量设置位置,通过距离变量设置相对移动和距离。
I leverage all of these reasons to draw the diagrams in this document. Whether I'm drawing 120 x 120 or 600 x 600, I use scale() to give me a 1.0 x 1.0 workspace. To place the results along the right column, such as in the discussion of cairo's drawing model, I use translate(). And to add the perspective view for the overlapping layers, I set up an arbitrary deformation with transform() on a cairo.Matrix().
我无论我绘制120 x 120 或600 x 600,我使用scale()给我一个1.0 x 1.0 的工作空间。为沿着正确的位置放置结果,就像在中讨论的那样,我
使用
I leverage all of these reasons to draw the diagrams in this document. Whether I'm drawing 120 x 120 or 600 x 600, I use scale() to give me a 1.0 x 1.0 workspace. To place the results along the right column, such as in the discussion of cairo's drawing model, I use translate(). And to add the perspective view for the overlapping layers, I set up an arbitrary deformation with transform() on a cairo.Matrix().
To understand your transforms, read them bottom to top, applying them to the point you're drawing. To figure out which transforms to create, think through this process in reverse. For example if I want my 1.0 x 1.0 workspace to be 100 x 100 pixels in the middle of a 120 x 120 pixel surface, I can set it up one of three ways:
Use the first when relevant because it is often the most readable; use the third when necessary to access additional control not available with the primary functions.
Be careful when trying to draw lines while under transform. Even if you set your line width while the scale factor was 1, the line width setting is always in user-coordinates and isn't modified by setting the scale. While you're operating under a scale, the width of your line is multiplied by that scale. To specify a width of a line in pixels, use device_to_user_distance() to turn a (1, 1) device-space distance into, for example, a (0.01, 0.01) user-space distance. Note that if your transform deforms the image there isn't necessarily a way to specify a line with a uniform width.
This wraps up the tutorial. It doesn't cover all functions in cairo, so for some advanced
lesser-used features, you'll need to look elsewhere. The code behind the examples uses a handful of techniques that aren't described within, so analyzing them may be a good first step. Other examples on cairographics.org lead in different directions. As with everything, there's a large gap between knowing the rules of the tool, and being able to use it well. The final section of this document provides some ideas to help you traverse parts of the gap.
This wraps up the tutorial. It doesn't cover all functions in cairo, so for some advanced
lesser-used features, you'll need to look elsewhere. The code behind the examples uses a handful of techniques that aren't described within, so analyzing them may be a good first step. Other examples on cairographics.org lead in different directions. As with everything, there's a large gap between knowing the rules of the tool, and being able to use it well. The final section of this document provides some ideas to help you traverse parts of the gap.
In the previous sections you should have built up a firm grasp of the operations cairo uses to create images. In this section I've put together a small handful of snippets I've found particularly useful or non-obvious. I'm still new to cairo myself, so there may be other better ways to do these things. If you find a better way, or find a cool way to do something else, let me know and perhaps I can incorporate it into these tips.
When you're working under a uniform scaling transform, you can't just use pixels for the width of your line. However it's easy to translate it with the following shortcut:
cr.set_line_width(max(cr.device_to_user_distance(pixel_width, pixel_width)))
When you're working under a deforming scale, you may wish to still have line widths that are uniform in device space. For this you should return to a uniform scale before you stroke the path. In the image, the arc on the left is stroked under a deformation, while the arc on the right is stroked under a uniform scale.
Cairo Tutorial: Tips and Tricks (Section #deform) cr.save() cr.scale(0.5, 1) cr.arc(0.5, 0.5, 0.40, 0, 2 * pi) cr.stroke() cr.translate(1, 0) cr.arc(0.5, 0.5, 0.40, 0, 2 * pi) cr.restore() cr.stroke()
When you try to center text letter by letter at various locations, you have to decide how you want to center it. For example the following code will actually center letters individually, leading to poor results when your letters are of different sizes. (Unlike most examples, here I assume a 26 x 1 workspace.)
Cairo Tutorial: Tips and Tricks (Section #center) for cx, letter in enumerate('AbCdEfGhIjKlMnOpQrStUvWxYz'): xbearing, ybearing, width, height, xadvance, yadvance = ( cr.text_extents(letter)) cr.move_to(cx + 0.5 - xbearing - width / 2, 0.5 - ybearing - height / 2) cr.show_text(letter)
Instead the vertical centering must be based on the general size of the font, thus keeping your baseline steady. Note that the exact positioning now depends on the metrics provided by the font itself, so the results are not necessarily the same from font to font.
Cairo Tutorial: Tips and Tricks (Section #baseline) fascent, fdescent, fheight, fxadvance, fyadvance = cr.font_extents() for cx, letter in enumerate('AbCdEfGhIjKlMnOpQrStUvWxYz'): xbearing, ybearing, width, height, xadvance, yadvance = ( cr.text_extents(letter)) cr.move_to(cx + 0.5 - xbearing - width / 2, 0.5 - fdescent + fheight / 2) cr.show_text(letter)