1 | /* Pango |
2 | * pango-gravity.c: Gravity routines |
3 | * |
4 | * Copyright (C) 2006, 2007 Red Hat Software |
5 | * |
6 | * This library is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Library General Public |
8 | * License as published by the Free Software Foundation; either |
9 | * version 2 of the License, or (at your option) any later version. |
10 | * |
11 | * This library is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | * Library General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Library General Public |
17 | * License along with this library; if not, write to the |
18 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
19 | * Boston, MA 02111-1307, USA. |
20 | */ |
21 | |
22 | #include "config.h" |
23 | |
24 | #include "pango-gravity.h" |
25 | |
26 | #include <math.h> |
27 | |
28 | /** |
29 | * pango_gravity_to_rotation: |
30 | * @gravity: gravity to query, should not be %PANGO_GRAVITY_AUTO |
31 | * |
32 | * Converts a `PangoGravity` value to its natural rotation in radians. |
33 | * |
34 | * Note that [method@Pango.Matrix.rotate] takes angle in degrees, not radians. |
35 | * So, to call [method@Pango.Matrix,rotate] with the output of this function |
36 | * you should multiply it by (180. / G_PI). |
37 | * |
38 | * Return value: the rotation value corresponding to @gravity. |
39 | * |
40 | * Since: 1.16 |
41 | */ |
42 | double |
43 | pango_gravity_to_rotation (PangoGravity gravity) |
44 | { |
45 | double rotation; |
46 | |
47 | g_return_val_if_fail (gravity != PANGO_GRAVITY_AUTO, 0); |
48 | |
49 | switch (gravity) |
50 | { |
51 | default: |
52 | case PANGO_GRAVITY_AUTO: /* shut gcc up */ |
53 | case PANGO_GRAVITY_SOUTH: rotation = 0; break; |
54 | case PANGO_GRAVITY_NORTH: rotation = G_PI; break; |
55 | case PANGO_GRAVITY_EAST: rotation = -G_PI_2; break; |
56 | case PANGO_GRAVITY_WEST: rotation = +G_PI_2; break; |
57 | } |
58 | |
59 | return rotation; |
60 | } |
61 | |
62 | /** |
63 | * pango_gravity_get_for_matrix: |
64 | * @matrix: (nullable): a `PangoMatrix` |
65 | * |
66 | * Finds the gravity that best matches the rotation component |
67 | * in a `PangoMatrix`. |
68 | * |
69 | * Return value: the gravity of @matrix, which will never be |
70 | * %PANGO_GRAVITY_AUTO, or %PANGO_GRAVITY_SOUTH if @matrix is %NULL |
71 | * |
72 | * Since: 1.16 |
73 | */ |
74 | PangoGravity |
75 | pango_gravity_get_for_matrix (const PangoMatrix *matrix) |
76 | { |
77 | PangoGravity gravity; |
78 | double x; |
79 | double y; |
80 | |
81 | if (!matrix) |
82 | return PANGO_GRAVITY_SOUTH; |
83 | |
84 | x = matrix->xy; |
85 | y = matrix->yy; |
86 | |
87 | if (fabs (x: x) > fabs (x: y)) |
88 | gravity = x > 0 ? PANGO_GRAVITY_WEST : PANGO_GRAVITY_EAST; |
89 | else |
90 | gravity = y < 0 ? PANGO_GRAVITY_NORTH : PANGO_GRAVITY_SOUTH; |
91 | |
92 | return gravity; |
93 | } |
94 | |
95 | |
96 | |
97 | typedef enum |
98 | { |
99 | PANGO_VERTICAL_DIRECTION_NONE, |
100 | PANGO_VERTICAL_DIRECTION_TTB, |
101 | PANGO_VERTICAL_DIRECTION_BTT |
102 | } PangoVerticalDirection; |
103 | |
104 | typedef struct { |
105 | /* PangoDirection */ |
106 | guint8 horiz_dir; /* Orientation in horizontal context */ |
107 | |
108 | /* PangoVerticalDirection */ |
109 | guint8 vert_dir; /* Orientation in vertical context */ |
110 | |
111 | /* PangoGravity */ |
112 | guint8 preferred_gravity; /* Preferred context gravity */ |
113 | |
114 | /* gboolean */ |
115 | guint8 wide; /* Whether script is mostly wide. |
116 | * Wide characters are upright (ie. |
117 | * not rotated) in foreign context */ |
118 | } PangoScriptProperties; |
119 | |
120 | #define NONE PANGO_VERTICAL_DIRECTION_NONE |
121 | #define TTB PANGO_VERTICAL_DIRECTION_TTB |
122 | #define BTT PANGO_VERTICAL_DIRECTION_BTT |
123 | |
124 | #define LTR PANGO_DIRECTION_LTR |
125 | #define RTL PANGO_DIRECTION_RTL |
126 | #define WEAK PANGO_DIRECTION_WEAK_LTR |
127 | |
128 | #define S PANGO_GRAVITY_SOUTH |
129 | #define E PANGO_GRAVITY_EAST |
130 | #define W PANGO_GRAVITY_WEST |
131 | |
132 | const PangoScriptProperties script_properties[] = |
133 | { /* ISO 15924 code */ |
134 | {LTR, NONE, S, FALSE}, /* Zyyy */ |
135 | {LTR, NONE, S, FALSE}, /* Qaai */ |
136 | {RTL, NONE, S, FALSE}, /* Arab */ |
137 | {LTR, NONE, S, FALSE}, /* Armn */ |
138 | {LTR, NONE, S, FALSE}, /* Beng */ |
139 | {LTR, TTB, E, TRUE }, /* Bopo */ |
140 | {LTR, NONE, S, FALSE}, /* Cher */ |
141 | {LTR, NONE, S, FALSE}, /* Qaac */ |
142 | {LTR, NONE, S, FALSE}, /* Cyrl (Cyrs) */ |
143 | {LTR, NONE, S, FALSE}, /* Dsrt */ |
144 | {LTR, NONE, S, FALSE}, /* Deva */ |
145 | {LTR, NONE, S, FALSE}, /* Ethi */ |
146 | {LTR, NONE, S, FALSE}, /* Geor (Geon, Geoa) */ |
147 | {LTR, NONE, S, FALSE}, /* Goth */ |
148 | {LTR, NONE, S, FALSE}, /* Grek */ |
149 | {LTR, NONE, S, FALSE}, /* Gujr */ |
150 | {LTR, NONE, S, FALSE}, /* Guru */ |
151 | {LTR, TTB, E, TRUE }, /* Hani */ |
152 | {LTR, TTB, E, TRUE }, /* Hang */ |
153 | {RTL, NONE, S, FALSE}, /* Hebr */ |
154 | {LTR, TTB, E, TRUE }, /* Hira */ |
155 | {LTR, NONE, S, FALSE}, /* Knda */ |
156 | {LTR, TTB, E, TRUE }, /* Kana */ |
157 | {LTR, NONE, S, FALSE}, /* Khmr */ |
158 | {LTR, NONE, S, FALSE}, /* Laoo */ |
159 | {LTR, NONE, S, FALSE}, /* Latn (Latf, Latg) */ |
160 | {LTR, NONE, S, FALSE}, /* Mlym */ |
161 | {WEAK,TTB, W, FALSE}, /* Mong */ |
162 | {LTR, NONE, S, FALSE}, /* Mymr */ |
163 | {LTR, BTT, W, FALSE}, /* Ogam */ |
164 | {LTR, NONE, S, FALSE}, /* Ital */ |
165 | {LTR, NONE, S, FALSE}, /* Orya */ |
166 | {LTR, NONE, S, FALSE}, /* Runr */ |
167 | {LTR, NONE, S, FALSE}, /* Sinh */ |
168 | {RTL, NONE, S, FALSE}, /* Syrc (Syrj, Syrn, Syre) */ |
169 | {LTR, NONE, S, FALSE}, /* Taml */ |
170 | {LTR, NONE, S, FALSE}, /* Telu */ |
171 | {RTL, NONE, S, FALSE}, /* Thaa */ |
172 | {LTR, NONE, S, FALSE}, /* Thai */ |
173 | {LTR, NONE, S, FALSE}, /* Tibt */ |
174 | {LTR, NONE, S, FALSE}, /* Cans */ |
175 | {LTR, TTB, S, TRUE }, /* Yiii */ |
176 | {LTR, NONE, S, FALSE}, /* Tglg */ |
177 | {LTR, NONE, S, FALSE}, /* Hano */ |
178 | {LTR, NONE, S, FALSE}, /* Buhd */ |
179 | {LTR, NONE, S, FALSE}, /* Tagb */ |
180 | |
181 | /* Unicode-4.0 additions */ |
182 | {LTR, NONE, S, FALSE}, /* Brai */ |
183 | {RTL, NONE, S, FALSE}, /* Cprt */ |
184 | {LTR, NONE, S, FALSE}, /* Limb */ |
185 | {LTR, NONE, S, FALSE}, /* Osma */ |
186 | {LTR, NONE, S, FALSE}, /* Shaw */ |
187 | {LTR, NONE, S, FALSE}, /* Linb */ |
188 | {LTR, NONE, S, FALSE}, /* Tale */ |
189 | {LTR, NONE, S, FALSE}, /* Ugar */ |
190 | |
191 | /* Unicode-4.1 additions */ |
192 | {LTR, NONE, S, FALSE}, /* Talu */ |
193 | {LTR, NONE, S, FALSE}, /* Bugi */ |
194 | {LTR, NONE, S, FALSE}, /* Glag */ |
195 | {LTR, NONE, S, FALSE}, /* Tfng */ |
196 | {LTR, NONE, S, FALSE}, /* Sylo */ |
197 | {LTR, NONE, S, FALSE}, /* Xpeo */ |
198 | {RTL, NONE, S, FALSE}, /* Khar */ |
199 | |
200 | /* Unicode-5.0 additions */ |
201 | {LTR, NONE, S, FALSE}, /* Zzzz */ |
202 | {LTR, NONE, S, FALSE}, /* Bali */ |
203 | {LTR, NONE, S, FALSE}, /* Xsux */ |
204 | {RTL, NONE, S, FALSE}, /* Phnx */ |
205 | {LTR, NONE, S, FALSE}, /* Phag */ |
206 | {RTL, NONE, S, FALSE}, /* Nkoo */ |
207 | |
208 | /* Unicode-5.1 additions */ |
209 | {LTR, NONE, S, FALSE}, /* Kali */ |
210 | {LTR, NONE, S, FALSE}, /* Lepc */ |
211 | {LTR, NONE, S, FALSE}, /* Rjng */ |
212 | {LTR, NONE, S, FALSE}, /* Sund */ |
213 | {LTR, NONE, S, FALSE}, /* Saur */ |
214 | {LTR, NONE, S, FALSE}, /* Cham */ |
215 | {LTR, NONE, S, FALSE}, /* Olck */ |
216 | {LTR, NONE, S, FALSE}, /* Vaii */ |
217 | {LTR, NONE, S, FALSE}, /* Cari */ |
218 | {LTR, NONE, S, FALSE}, /* Lyci */ |
219 | {RTL, NONE, S, FALSE}, /* Lydi */ |
220 | |
221 | /* Unicode-5.2 additions */ |
222 | {RTL, NONE, S, FALSE}, /* Avst */ |
223 | {LTR, NONE, S, FALSE}, /* Bamu */ |
224 | {LTR, NONE, S, FALSE}, /* Egyp */ |
225 | {RTL, NONE, S, FALSE}, /* Armi */ |
226 | {RTL, NONE, S, FALSE}, /* Phli */ |
227 | {RTL, NONE, S, FALSE}, /* Prti */ |
228 | {LTR, NONE, S, FALSE}, /* Java */ |
229 | {LTR, NONE, S, FALSE}, /* Kthi */ |
230 | {LTR, NONE, S, FALSE}, /* Lisu */ |
231 | {LTR, NONE, S, FALSE}, /* Mtei */ |
232 | {RTL, NONE, S, FALSE}, /* Sarb */ |
233 | {RTL, NONE, S, FALSE}, /* Orkh */ |
234 | {RTL, TTB, S, FALSE}, /* Samr */ |
235 | {LTR, NONE, S, FALSE}, /* Lana */ |
236 | {LTR, NONE, S, FALSE}, /* Tavt */ |
237 | |
238 | /* Unicode-6.0 additions */ |
239 | {LTR, NONE, S, FALSE}, /* Batk */ |
240 | {LTR, NONE, S, FALSE}, /* Brah */ |
241 | {RTL, NONE, S, FALSE}, /* Mand */ |
242 | |
243 | /* Unicode-6.1 additions */ |
244 | {LTR, NONE, S, FALSE}, /* Cakm */ |
245 | {RTL, NONE, S, FALSE}, /* Merc */ |
246 | {RTL, NONE, S, FALSE}, /* Mero */ |
247 | {LTR, NONE, S, FALSE}, /* Plrd */ |
248 | {LTR, NONE, S, FALSE}, /* Shrd */ |
249 | {LTR, NONE, S, FALSE}, /* Sora */ |
250 | {LTR, NONE, S, FALSE}, /* Takr */ |
251 | |
252 | /* Unicode-7.0 additions */ |
253 | {LTR, NONE, S, FALSE}, /* Bass */ |
254 | {LTR, NONE, S, FALSE}, /* Aghb */ |
255 | {LTR, NONE, S, FALSE}, /* Dupl */ |
256 | {LTR, NONE, S, FALSE}, /* Elba */ |
257 | {LTR, NONE, S, FALSE}, /* Gran */ |
258 | {LTR, NONE, S, FALSE}, /* Khoj */ |
259 | {LTR, NONE, S, FALSE}, /* Sind */ |
260 | {LTR, NONE, S, FALSE}, /* Lina */ |
261 | {LTR, NONE, S, FALSE}, /* Mahj */ |
262 | {RTL, NONE, S, FALSE}, /* Mani */ |
263 | {RTL, NONE, S, FALSE}, /* Mend */ |
264 | {LTR, NONE, S, FALSE}, /* Modi */ |
265 | {LTR, NONE, S, FALSE}, /* Mroo */ |
266 | {RTL, NONE, S, FALSE}, /* Nbat */ |
267 | {RTL, NONE, S, FALSE}, /* Narb */ |
268 | {LTR, NONE, S, FALSE}, /* Perm */ |
269 | {LTR, NONE, S, FALSE}, /* Hmng */ |
270 | {RTL, NONE, S, FALSE}, /* Palm */ |
271 | {LTR, NONE, S, FALSE}, /* Pauc */ |
272 | {RTL, NONE, S, FALSE}, /* Phlp */ |
273 | {LTR, NONE, S, FALSE}, /* Sidd */ |
274 | {LTR, NONE, S, FALSE}, /* Tirh */ |
275 | {LTR, NONE, S, FALSE}, /* Wara */ |
276 | |
277 | /* Unicode-8.0 additions */ |
278 | {LTR, NONE, S, FALSE}, /* Ahom */ |
279 | {LTR, NONE, S, FALSE}, /* Hluw */ |
280 | {RTL, NONE, S, FALSE}, /* Hatr */ |
281 | {LTR, NONE, S, FALSE}, /* Mult */ |
282 | {LTR, NONE, S, FALSE}, /* Hung */ |
283 | {LTR, NONE, S, FALSE}, /* Sgnw */ |
284 | |
285 | /* Unicode-9.0 additions */ |
286 | {RTL, NONE, S, FALSE}, /* Adlm */ |
287 | {LTR, NONE, S, FALSE}, /* Bhks */ |
288 | {LTR, NONE, S, FALSE}, /* Marc */ |
289 | {LTR, NONE, S, FALSE}, /* Newa */ |
290 | {LTR, NONE, S, FALSE}, /* Osge */ |
291 | {LTR, NONE, S, FALSE}, /* Tang */ |
292 | |
293 | /* Unicode-10.0 additions */ |
294 | {LTR, NONE, S, FALSE}, /* Gonm */ |
295 | {LTR, NONE, S, FALSE}, /* Nshu */ |
296 | {LTR, NONE, S, FALSE}, /* Soyo */ |
297 | {LTR, NONE, S, FALSE}, /* Zanb */ |
298 | |
299 | /* Unicode-11.0 additions */ |
300 | {LTR, NONE, S, FALSE}, /* Dogr */ |
301 | {LTR, NONE, S, FALSE}, /* Gong */ |
302 | {RTL, NONE, S, FALSE}, /* Rohg */ |
303 | {LTR, NONE, S, FALSE}, /* Maka */ |
304 | {LTR, NONE, S, FALSE}, /* Medf */ |
305 | {RTL, NONE, S, FALSE}, /* Sogo */ |
306 | {RTL, NONE, S, FALSE}, /* Sogd */ |
307 | |
308 | /* Unicode-12.0 additions */ |
309 | {RTL, NONE, S, FALSE}, /* Elym */ |
310 | {LTR, NONE, S, FALSE}, /* Nand */ |
311 | {LTR, NONE, S, FALSE}, /* Rohg */ |
312 | {LTR, NONE, S, FALSE}, /* Wcho */ |
313 | |
314 | /* Unicode-13.0 additions */ |
315 | {RTL, NONE, S, FALSE}, /* Chrs */ |
316 | {LTR, NONE, S, FALSE}, /* Diak */ |
317 | {LTR, NONE, S, FALSE}, /* Kits */ |
318 | {RTL, NONE, S, FALSE}, /* Yezi */ |
319 | |
320 | {LTR, NONE, S, FALSE}, /* Cpmn */ |
321 | {RTL, NONE, S, FALSE}, /* Ougr */ |
322 | {LTR, NONE, S, FALSE}, /* Tnsa */ |
323 | {LTR, NONE, S, FALSE}, /* Toto */ |
324 | {LTR, NONE, S, FALSE}, /* Vith */ |
325 | }; |
326 | |
327 | #undef NONE |
328 | #undef TTB |
329 | #undef BTT |
330 | |
331 | #undef LTR |
332 | #undef RTL |
333 | #undef WEAK |
334 | |
335 | #undef S |
336 | #undef E |
337 | #undef N |
338 | #undef W |
339 | |
340 | static PangoScriptProperties |
341 | get_script_properties (PangoScript script) |
342 | { |
343 | g_return_val_if_fail (script >= 0, script_properties[0]); |
344 | |
345 | if ((guint)script >= G_N_ELEMENTS (script_properties)) |
346 | return script_properties[0]; |
347 | |
348 | return script_properties[script]; |
349 | } |
350 | |
351 | /** |
352 | * pango_gravity_get_for_script: |
353 | * @script: `PangoScript` to query |
354 | * @base_gravity: base gravity of the paragraph |
355 | * @hint: orientation hint |
356 | * |
357 | * Returns the gravity to use in laying out a `PangoItem`. |
358 | * |
359 | * The gravity is determined based on the script, base gravity, and hint. |
360 | * |
361 | * If @base_gravity is %PANGO_GRAVITY_AUTO, it is first replaced with the |
362 | * preferred gravity of @script. To get the preferred gravity of a script, |
363 | * pass %PANGO_GRAVITY_AUTO and %PANGO_GRAVITY_HINT_STRONG in. |
364 | * |
365 | * Return value: resolved gravity suitable to use for a run of text |
366 | * with @script |
367 | * |
368 | * Since: 1.16 |
369 | */ |
370 | PangoGravity |
371 | pango_gravity_get_for_script (PangoScript script, |
372 | PangoGravity base_gravity, |
373 | PangoGravityHint hint) |
374 | { |
375 | PangoScriptProperties props = get_script_properties (script); |
376 | |
377 | return pango_gravity_get_for_script_and_width (script, wide: props.wide, |
378 | base_gravity, hint); |
379 | } |
380 | |
381 | /** |
382 | * pango_gravity_get_for_script_and_width: |
383 | * @script: `PangoScript` to query |
384 | * @wide: %TRUE for wide characters as returned by g_unichar_iswide() |
385 | * @base_gravity: base gravity of the paragraph |
386 | * @hint: orientation hint |
387 | * |
388 | * Returns the gravity to use in laying out a single character |
389 | * or `PangoItem`. |
390 | * |
391 | * The gravity is determined based on the script, East Asian width, |
392 | * base gravity, and hint, |
393 | * |
394 | * This function is similar to [func@Pango.Gravity.get_for_script] except |
395 | * that this function makes a distinction between narrow/half-width and |
396 | * wide/full-width characters also. Wide/full-width characters always |
397 | * stand *upright*, that is, they always take the base gravity, |
398 | * whereas narrow/full-width characters are always rotated in vertical |
399 | * context. |
400 | * |
401 | * If @base_gravity is %PANGO_GRAVITY_AUTO, it is first replaced with the |
402 | * preferred gravity of @script. |
403 | * |
404 | * Return value: resolved gravity suitable to use for a run of text |
405 | * with @script and @wide. |
406 | * |
407 | * Since: 1.26 |
408 | */ |
409 | PangoGravity |
410 | pango_gravity_get_for_script_and_width (PangoScript script, |
411 | gboolean wide, |
412 | PangoGravity base_gravity, |
413 | PangoGravityHint hint) |
414 | { |
415 | PangoScriptProperties props = get_script_properties (script); |
416 | gboolean vertical; |
417 | |
418 | if (G_UNLIKELY (base_gravity == PANGO_GRAVITY_AUTO)) |
419 | base_gravity = props.preferred_gravity; |
420 | |
421 | vertical = PANGO_GRAVITY_IS_VERTICAL (base_gravity); |
422 | |
423 | /* Everything is designed such that a system with no vertical support |
424 | * renders everything correctly horizontally. So, if not in a vertical |
425 | * gravity, base and resolved gravities are always the same. |
426 | * |
427 | * Wide characters are always upright. |
428 | */ |
429 | if (G_LIKELY (!vertical || wide)) |
430 | return base_gravity; |
431 | |
432 | /* If here, we have a narrow character in a vertical gravity setting. |
433 | * Resolve depending on the hint. |
434 | */ |
435 | switch (hint) |
436 | { |
437 | default: |
438 | case PANGO_GRAVITY_HINT_NATURAL: |
439 | if (props.vert_dir == PANGO_VERTICAL_DIRECTION_NONE) |
440 | return PANGO_GRAVITY_SOUTH; |
441 | if ((base_gravity == PANGO_GRAVITY_EAST) ^ |
442 | (props.vert_dir == PANGO_VERTICAL_DIRECTION_BTT)) |
443 | return PANGO_GRAVITY_SOUTH; |
444 | else |
445 | return PANGO_GRAVITY_NORTH; |
446 | |
447 | case PANGO_GRAVITY_HINT_STRONG: |
448 | return base_gravity; |
449 | |
450 | case PANGO_GRAVITY_HINT_LINE: |
451 | if ((base_gravity == PANGO_GRAVITY_EAST) ^ |
452 | (props.horiz_dir == PANGO_DIRECTION_RTL)) |
453 | return PANGO_GRAVITY_SOUTH; |
454 | else |
455 | return PANGO_GRAVITY_NORTH; |
456 | } |
457 | } |
458 | |