Skip to content

Commit 5255373

Browse files
committed
WIP add more pango examples
1 parent 86a06a5 commit 5255373

File tree

3 files changed

+555
-0
lines changed

3 files changed

+555
-0
lines changed

build.zig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ const EXAMPLES = [_][]const u8{
2222
"image_pattern",
2323
"mask",
2424
"multi_segment_caps",
25+
"pango_shape",
2526
"pango_simple",
27+
"pango_twisted",
2628
"rounded_rectangle",
2729
"save_and_restore",
2830
"set_line_cap",

examples/pango_shape.zig

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
//! Example code to show how to use pangocairo to render arbitrary shapes inside
2+
//! a text layout, positioned by Pango.
3+
//! https://gitlab.gnome.org/GNOME/pango/-/blob/master/examples/cairoshape.c
4+
const std = @import("std");
5+
const log = std.log;
6+
const pi = std.math.pi;
7+
const cos = std.math.cos;
8+
const cairo = @import("cairo");
9+
const pc = @import("pangocairo");
10+
11+
const BULLET = "•";
12+
13+
const text =
14+
\\The GNOME project provides two things:
15+
\\
16+
\\ • The GNOME desktop environment
17+
\\ • The GNOME development platform
18+
\\ • Planet GNOME
19+
;
20+
21+
const path =
22+
\\M 86.068,1 C 61.466,0 56.851,35.041 70.691,35.041 C 84.529,35.041 110.671,0 86.068,0 z
23+
\\M 45.217,30.699 C 52.586,31.149 60.671,2.577 46.821,4.374 C 32.976,6.171 37.845,30.249 45.217,30.699 z
24+
\\M 11.445,48.453 C 16.686,46.146 12.12,23.581 3.208,29.735 C -5.7,35.89 6.204,50.759 11.445,48.453 z
25+
\\M 26.212,36.642 C 32.451,35.37 32.793,9.778 21.667,14.369 C 10.539,18.961 19.978,37.916 26.212,36.642 L 26.212,36.642 z
26+
\\M 58.791,93.913 C 59.898,102.367 52.589,106.542 45.431,101.092 C 22.644,83.743 83.16,75.088 79.171,51.386 C 75.86,31.712 15.495,37.769 8.621,68.553 C 3.968,89.374 27.774,118.26 52.614,118.26 C 64.834,118.26 78.929,107.226 81.566,93.248 C 83.58,82.589 57.867,86.86 58.791,93.913 L 58.791,93.913 z
27+
;
28+
29+
// I don't know if the multiline string works or I have to use a single string
30+
// const path = "M 86.068,1 C 61.466,0 56.851,35.041 70.691,35.041 C 84.529,35.041 110.671,0 86.068,0 z M 45.217,30.699 C 52.586,31.149 60.671,2.577 46.821,4.374 C 32.976,6.171 37.845,30.249 45.217,30.699 z M 11.445,48.453 C 16.686,46.146 12.12,23.581 3.208,29.735 C -5.7,35.89 6.204,50.759 11.445,48.453 z M 26.212,36.642 C 32.451,35.37 32.793,9.778 21.667,14.369 C 10.539,18.961 19.978,37.916 26.212,36.642 L 26.212,36.642 z M 58.791,93.913 C 59.898,102.367 52.589,106.542 45.431,101.092 C 22.644,83.743 83.16,75.088 79.171,51.386 C 75.86,31.712 15.495,37.769 8.621,68.553 C 3.968,89.374 27.774,118.26 52.614,118.26 C 64.834,118.26 78.929,107.226 81.566,93.248 C 83.58,82.589 57.867,86.86 58.791,93.913 L 58.791,93.913 z ";
31+
32+
// TODO: add render method? (it would replace the miniSvgRender function)
33+
const MiniSvg = struct {
34+
width: f64,
35+
height: f64,
36+
path: []const u8,
37+
};
38+
39+
var gnome_foot_logo = MiniSvg{
40+
.width = 96.2152,
41+
.height = 118.26,
42+
.path = path,
43+
};
44+
45+
// TODO: miniSvgRender
46+
fn miniSvgRender(shape: *MiniSvg, cr: ?cairo.CContext, do_path: c_int) void {
47+
log.debug("miniSvgRender", .{});
48+
if (do_path == 1) {
49+
log.debug("do_path is TRUE", .{});
50+
} else {
51+
log.debug("do_path is FALSE", .{});
52+
}
53+
}
54+
55+
fn shapeRenderer(cr: ?cairo.CContext, attr: pc.PangoAttrShape, do_path: c_int, data: ?*c_void) callconv(.C) void {
56+
log.debug("shapeRenderer", .{});
57+
if (do_path == 1) {
58+
log.debug("do_path is TRUE", .{});
59+
} else {
60+
log.debug("do_path is FALSE", .{});
61+
}
62+
// log.debug("attr {}", .{ attr });
63+
// log.debug("PangoAttrShape {}", .{ attr.* });
64+
log.debug("PangoAttrShape.data {}", .{attr.*.data});
65+
// TODO: how to cast PangoAttrShape.data into *MiniSvg ? The alignment
66+
// doesn't match.
67+
// const shape = @ptrCast(*MiniSvg, attr.*.data.?);
68+
// const shape = (MiniSvg *) attr.data; // @ptrCast a MiniSvg ?
69+
70+
log.debug("ink_rect.width {}", .{attr.*.ink_rect.width});
71+
log.debug("ink_rect.height {}", .{attr.*.ink_rect.height});
72+
73+
// scale_x = (double) attr->ink_rect.width / (PANGO_SCALE * shape->width );
74+
// scale_y = (double) attr->ink_rect.height / (PANGO_SCALE * shape->height);
75+
76+
// cr.relMoveTo((double) attr->ink_rect.x / PANGO_SCALE, (double) attr->ink_rect.y / PANGO_SCALE);
77+
// cr.scale(scale_x, scale_y);
78+
// miniSvgRender(shape, cr, do_path);
79+
// this should probably be:
80+
// miniSvg.render(cr, do_path);
81+
82+
// @panic("TODO: remove me when shapeRenderer is correct");
83+
}
84+
85+
// TODO: how to return a ?*c_void type?
86+
// fn pangoAttrDataCopyFunc(user_data: ?*const c_void) callconv(.C) ?*c_void {
87+
// log.debug("pangoAttrDataCopyFunc", .{});
88+
// log.debug("user_data {}", .{user_data});
89+
// return @ptrCast(?*c_void, user_data);
90+
// }
91+
const pangoAttrDataCopyFunc: ?pc.PangoAttrDataCopyFunc = null;
92+
93+
fn gDestroyNotify(data: ?*c_void) callconv(.C) void {
94+
log.debug("gDestroyNotify", .{});
95+
// log.debug("data {}", .{data.?.*}); // data.? is opaque
96+
}
97+
// const gDestroyNotify: ?pc.GDestroyNotify = null;
98+
99+
fn getLayout(cr: *cairo.Context) !pc.Layout {
100+
var ink_rect = try pc.Rectangle.new(1 * pc.SCALE, -11 * pc.SCALE, 8 * pc.SCALE, 10 * pc.SCALE);
101+
var logical_rect = try pc.Rectangle.new(0 * pc.SCALE, -12 * pc.SCALE, 10 * pc.SCALE, 12 * pc.SCALE);
102+
103+
var layout = try pc.Layout.create(cr);
104+
105+
var ctx = try layout.getContext();
106+
ctx.setShapeRenderer(shapeRenderer, null, null);
107+
layout.setText(text);
108+
109+
var attrs = try pc.AttrList.new();
110+
defer attrs.destroy();
111+
112+
var idx = std.mem.indexOf(u8, text[0..], BULLET);
113+
while (idx != null) {
114+
const str = text[idx.?..];
115+
// log.debug("idx {} str.len {}", .{idx, str.len});
116+
var attr = try pc.Attribute.newShapeWithData(MiniSvg, &ink_rect, &logical_rect, &gnome_foot_logo, pangoAttrDataCopyFunc, gDestroyNotify);
117+
// TODO: move this ptrCast and intCast to pango.zig
118+
// https://stackoverflow.com/questions/18659120/subtracting-two-strings-in-c
119+
const str_addr = @intCast(c_uint, @ptrToInt(str.ptr));
120+
const text_addr = @intCast(c_uint, @ptrToInt(text));
121+
attr.c_ptr.*.start_index = str_addr - text_addr;
122+
attr.c_ptr.*.end_index = attr.c_ptr.*.start_index + @intCast(c_uint, BULLET.len);
123+
attrs.insert(&attr);
124+
125+
// find index for next iteration
126+
const i_rel = std.mem.indexOf(u8, text[idx.? + BULLET.len ..], BULLET);
127+
if (i_rel == null) {
128+
idx = null;
129+
} else {
130+
idx = idx.? + i_rel.? + 1;
131+
}
132+
}
133+
134+
layout.setAttributes(&attrs);
135+
return layout;
136+
}
137+
138+
fn drawText(cr: *cairo.Context, width: ?f64, height: ?f64) !pc.Size {
139+
log.info("=== drawText ===", .{});
140+
var layout = try getLayout(cr);
141+
defer layout.destroy();
142+
143+
var size = pc.Size{ .width = 0.0, .height = 0.0 };
144+
const margin = 10.0;
145+
146+
if ((width != null) or (height != null)) {
147+
const original_size = layout.getPixelSize();
148+
if (width != null) {
149+
size.width = original_size.width + 2.0 * margin;
150+
}
151+
if (height != null) {
152+
size.height = original_size.height + 2.0 * margin;
153+
}
154+
}
155+
156+
cr.moveTo(margin, margin);
157+
layout.show(cr);
158+
159+
return size;
160+
}
161+
162+
pub fn main() !void {
163+
// First create and use a 0x0 surface, to measure how large the final
164+
// surface needs to be.
165+
var surface = try cairo.Surface.image(0.0, 0.0);
166+
defer surface.destroy();
167+
168+
var cr = try cairo.Context.create(&surface);
169+
defer cr.destroy();
170+
171+
const size = try drawText(&cr, null, null);
172+
173+
// TODO: Now create the final surface and draw to it. Reuse surface and cr?
174+
var surface2 = try cairo.Surface.image(@floatToInt(u16, size.width), @floatToInt(u16, size.height));
175+
defer surface2.destroy();
176+
177+
var cr2 = try cairo.Context.create(&surface);
178+
defer cr2.destroy();
179+
180+
cr2.setSourceRgb(1.0, 1.0, 1.0); // white
181+
cr2.paint();
182+
183+
cr2.setSourceRgb(0.0, 0.0, 0.5);
184+
const size2 = try drawText(&cr2, size.width, size.height);
185+
186+
_ = surface2.writeToPng("examples/generated/pango_shape.png");
187+
}

0 commit comments

Comments
 (0)