1 | /* Pango |
2 | * pango-utils.c: Utilities for internal functions and modules |
3 | * |
4 | * Copyright (C) 2000 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 | #include <errno.h> |
24 | #include <string.h> |
25 | #include <stdlib.h> |
26 | #include <math.h> |
27 | #include <locale.h> |
28 | |
29 | #include "pango-font.h" |
30 | #include "pango-features.h" |
31 | #include "pango-impl-utils.h" |
32 | #include "pango-utils-internal.h" |
33 | #include "pango-utils-private.h" |
34 | |
35 | #include <glib/gstdio.h> |
36 | |
37 | #ifndef HAVE_FLOCKFILE |
38 | # define flockfile(f) (void)1 |
39 | # define funlockfile(f) (void)1 |
40 | # define getc_unlocked(f) getc(f) |
41 | #endif /* !HAVE_FLOCKFILE */ |
42 | |
43 | #ifdef G_OS_WIN32 |
44 | |
45 | #include <sys/types.h> |
46 | |
47 | #define STRICT |
48 | #include <windows.h> |
49 | |
50 | #endif |
51 | |
52 | /** |
53 | * pango_version: |
54 | * |
55 | * Returns the encoded version of Pango available at run-time. |
56 | * |
57 | * This is similar to the macro %PANGO_VERSION except that the macro |
58 | * returns the encoded version available at compile-time. A version |
59 | * number can be encoded into an integer using PANGO_VERSION_ENCODE(). |
60 | * |
61 | * Returns: The encoded version of Pango library available at run time. |
62 | * |
63 | * Since: 1.16 |
64 | */ |
65 | int |
66 | pango_version (void) |
67 | { |
68 | return PANGO_VERSION; |
69 | } |
70 | |
71 | /** |
72 | * pango_version_string: |
73 | * |
74 | * Returns the version of Pango available at run-time. |
75 | * |
76 | * This is similar to the macro %PANGO_VERSION_STRING except that the |
77 | * macro returns the version available at compile-time. |
78 | * |
79 | * Returns: A string containing the version of Pango library available |
80 | * at run time. The returned string is owned by Pango and should not |
81 | * be modified or freed. |
82 | * |
83 | * Since: 1.16 |
84 | */ |
85 | const char * |
86 | pango_version_string (void) |
87 | { |
88 | return PANGO_VERSION_STRING; |
89 | } |
90 | |
91 | /** |
92 | * pango_version_check: |
93 | * @required_major: the required major version |
94 | * @required_minor: the required minor version |
95 | * @required_micro: the required major version |
96 | * |
97 | * Checks that the Pango library in use is compatible with the |
98 | * given version. |
99 | * |
100 | * Generally you would pass in the constants %PANGO_VERSION_MAJOR, |
101 | * %PANGO_VERSION_MINOR, %PANGO_VERSION_MICRO as the three arguments |
102 | * to this function; that produces a check that the library in use at |
103 | * run-time is compatible with the version of Pango the application or |
104 | * module was compiled against. |
105 | * |
106 | * Compatibility is defined by two things: first the version |
107 | * of the running library is newer than the version |
108 | * @required_major.required_minor.@required_micro. Second |
109 | * the running library must be binary compatible with the |
110 | * version @required_major.required_minor.@required_micro |
111 | * (same major version.) |
112 | * |
113 | * For compile-time version checking use PANGO_VERSION_CHECK(). |
114 | * |
115 | * Return value: (nullable): %NULL if the Pango library is compatible |
116 | * with the given version, or a string describing the version |
117 | * mismatch. The returned string is owned by Pango and should not |
118 | * be modified or freed. |
119 | * |
120 | * Since: 1.16 |
121 | */ |
122 | const gchar* |
123 | pango_version_check (int required_major, |
124 | int required_minor, |
125 | int required_micro) |
126 | { |
127 | gint pango_effective_micro = 100 * PANGO_VERSION_MINOR + PANGO_VERSION_MICRO; |
128 | gint required_effective_micro = 100 * required_minor + required_micro; |
129 | |
130 | if (required_major > PANGO_VERSION_MAJOR) |
131 | return "Pango version too old (major mismatch)" ; |
132 | if (required_major < PANGO_VERSION_MAJOR) |
133 | return "Pango version too new (major mismatch)" ; |
134 | if (required_effective_micro < pango_effective_micro - PANGO_BINARY_AGE) |
135 | return "Pango version too new (micro mismatch)" ; |
136 | if (required_effective_micro > pango_effective_micro) |
137 | return "Pango version too old (micro mismatch)" ; |
138 | return NULL; |
139 | } |
140 | |
141 | /** |
142 | * pango_trim_string: |
143 | * @str: a string |
144 | * |
145 | * Trims leading and trailing whitespace from a string. |
146 | * |
147 | * Return value: A newly-allocated string that must be freed with g_free() |
148 | * |
149 | * Deprecated: 1.38 |
150 | */ |
151 | char * |
152 | pango_trim_string (const char *str) |
153 | { |
154 | return _pango_trim_string (str); |
155 | } |
156 | |
157 | char * |
158 | _pango_trim_string (const char *str) |
159 | { |
160 | int len; |
161 | |
162 | g_return_val_if_fail (str != NULL, NULL); |
163 | |
164 | while (*str && g_ascii_isspace (*str)) |
165 | str++; |
166 | |
167 | len = strlen (s: str); |
168 | while (len > 0 && g_ascii_isspace (str[len-1])) |
169 | len--; |
170 | |
171 | return g_strndup (str, n: len); |
172 | } |
173 | |
174 | /** |
175 | * pango_split_file_list: |
176 | * @str: a %G_SEARCHPATH_SEPARATOR separated list of filenames |
177 | * |
178 | * Splits a %G_SEARCHPATH_SEPARATOR-separated list of files, stripping |
179 | * white space and substituting ~/ with $HOME/. |
180 | * |
181 | * Return value: (transfer full) (array zero-terminated=1): a list of |
182 | * strings to be freed with g_strfreev() |
183 | * |
184 | * Deprecated: 1.38 |
185 | */ |
186 | char ** |
187 | pango_split_file_list (const char *str) |
188 | { |
189 | int i = 0; |
190 | int j; |
191 | char **files; |
192 | |
193 | files = g_strsplit (string: str, G_SEARCHPATH_SEPARATOR_S, max_tokens: -1); |
194 | |
195 | while (files[i]) |
196 | { |
197 | char *file = _pango_trim_string (str: files[i]); |
198 | |
199 | /* If the resulting file is empty, skip it */ |
200 | if (file[0] == '\0') |
201 | { |
202 | g_free(mem: file); |
203 | g_free (mem: files[i]); |
204 | |
205 | for (j = i + 1; files[j]; j++) |
206 | files[j - 1] = files[j]; |
207 | |
208 | files[j - 1] = NULL; |
209 | |
210 | continue; |
211 | } |
212 | #ifndef G_OS_WIN32 |
213 | /* '~' is a quite normal and common character in file names on |
214 | * Windows, especially in the 8.3 versions of long file names, which |
215 | * still occur now and then. Also, few Windows user are aware of the |
216 | * Unix shell convention that '~' stands for the home directory, |
217 | * even if they happen to have a home directory. |
218 | */ |
219 | if (file[0] == '~' && file[1] == G_DIR_SEPARATOR) |
220 | { |
221 | char *tmp = g_strconcat (string1: g_get_home_dir(), file + 1, NULL); |
222 | g_free (mem: file); |
223 | file = tmp; |
224 | } |
225 | else if (file[0] == '~' && file[1] == '\0') |
226 | { |
227 | g_free (mem: file); |
228 | file = g_strdup (str: g_get_home_dir()); |
229 | } |
230 | #endif |
231 | g_free (mem: files[i]); |
232 | files[i] = file; |
233 | |
234 | i++; |
235 | } |
236 | |
237 | return files; |
238 | } |
239 | |
240 | /** |
241 | * pango_read_line: |
242 | * @stream: a stdio stream |
243 | * @str: `GString` buffer into which to write the result |
244 | * |
245 | * Reads an entire line from a file into a buffer. |
246 | * |
247 | * Lines may be delimited with '\n', '\r', '\n\r', or '\r\n'. The delimiter |
248 | * is not written into the buffer. Text after a '#' character is treated as |
249 | * a comment and skipped. '\' can be used to escape a # character. |
250 | * '\' proceeding a line delimiter combines adjacent lines. A '\' proceeding |
251 | * any other character is ignored and written into the output buffer |
252 | * unmodified. |
253 | * |
254 | * Return value: 0 if the stream was already at an %EOF character, |
255 | * otherwise the number of lines read (this is useful for maintaining |
256 | * a line number counter which doesn't combine lines with '\') |
257 | * |
258 | * Deprecated: 1.38 |
259 | */ |
260 | gint |
261 | pango_read_line (FILE *stream, GString *str) |
262 | { |
263 | gboolean quoted = FALSE; |
264 | gboolean = FALSE; |
265 | int n_read = 0; |
266 | int lines = 1; |
267 | |
268 | flockfile (stream: stream); |
269 | |
270 | g_string_truncate (string: str, len: 0); |
271 | |
272 | while (1) |
273 | { |
274 | int c; |
275 | |
276 | c = getc_unlocked (fp: stream); |
277 | |
278 | if (c == EOF) |
279 | { |
280 | if (quoted) |
281 | g_string_append_c (str, '\\'); |
282 | |
283 | goto done; |
284 | } |
285 | else |
286 | n_read++; |
287 | |
288 | if (quoted) |
289 | { |
290 | quoted = FALSE; |
291 | |
292 | switch (c) |
293 | { |
294 | case '#': |
295 | g_string_append_c (str, '#'); |
296 | break; |
297 | case '\r': |
298 | case '\n': |
299 | { |
300 | int next_c = getc_unlocked (fp: stream); |
301 | |
302 | if (!(next_c == EOF || |
303 | (c == '\r' && next_c == '\n') || |
304 | (c == '\n' && next_c == '\r'))) |
305 | ungetc (c: next_c, stream: stream); |
306 | |
307 | lines++; |
308 | |
309 | break; |
310 | } |
311 | default: |
312 | g_string_append_c (str, '\\'); |
313 | g_string_append_c (str, c); |
314 | } |
315 | } |
316 | else |
317 | { |
318 | switch (c) |
319 | { |
320 | case '#': |
321 | comment = TRUE; |
322 | break; |
323 | case '\\': |
324 | if (!comment) |
325 | quoted = TRUE; |
326 | break; |
327 | case '\r': |
328 | case '\n': |
329 | { |
330 | int next_c = getc_unlocked (fp: stream); |
331 | |
332 | if (!(next_c == EOF || |
333 | (c == '\r' && next_c == '\n') || |
334 | (c == '\n' && next_c == '\r'))) |
335 | ungetc (c: next_c, stream: stream); |
336 | |
337 | goto done; |
338 | } |
339 | default: |
340 | if (!comment) |
341 | g_string_append_c (str, c); |
342 | } |
343 | } |
344 | } |
345 | |
346 | done: |
347 | |
348 | funlockfile (stream: stream); |
349 | |
350 | return (n_read > 0) ? lines : 0; |
351 | } |
352 | |
353 | /** |
354 | * pango_skip_space: |
355 | * @pos: (inout): in/out string position |
356 | * |
357 | * Skips 0 or more characters of white space. |
358 | * |
359 | * Return value: %FALSE if skipping the white space leaves |
360 | * the position at a '\0' character. |
361 | * |
362 | * Deprecated: 1.38 |
363 | */ |
364 | gboolean |
365 | pango_skip_space (const char **pos) |
366 | { |
367 | const char *p = *pos; |
368 | |
369 | while (g_ascii_isspace (*p)) |
370 | p++; |
371 | |
372 | *pos = p; |
373 | |
374 | return !(*p == '\0'); |
375 | } |
376 | |
377 | /** |
378 | * pango_scan_word: |
379 | * @pos: (inout): in/out string position |
380 | * @out: a `GString` into which to write the result |
381 | * |
382 | * Scans a word into a `GString` buffer. |
383 | * |
384 | * A word consists of [A-Za-z_] followed by zero or more |
385 | * [A-Za-z_0-9]. Leading white space is skipped. |
386 | * |
387 | * Return value: %FALSE if a parse error occurred |
388 | * |
389 | * Deprecated: 1.38 |
390 | */ |
391 | gboolean |
392 | pango_scan_word (const char **pos, GString *out) |
393 | { |
394 | const char *p = *pos; |
395 | |
396 | while (g_ascii_isspace (*p)) |
397 | p++; |
398 | |
399 | if (!((*p >= 'A' && *p <= 'Z') || |
400 | (*p >= 'a' && *p <= 'z') || |
401 | *p == '_')) |
402 | return FALSE; |
403 | |
404 | g_string_truncate (string: out, len: 0); |
405 | g_string_append_c (out, *p); |
406 | p++; |
407 | |
408 | while ((*p >= 'A' && *p <= 'Z') || |
409 | (*p >= 'a' && *p <= 'z') || |
410 | (*p >= '0' && *p <= '9') || |
411 | *p == '_') |
412 | { |
413 | g_string_append_c (out, *p); |
414 | p++; |
415 | } |
416 | |
417 | *pos = p; |
418 | |
419 | return TRUE; |
420 | } |
421 | |
422 | /** |
423 | * pango_scan_string: |
424 | * @pos: (inout): in/out string position |
425 | * @out: a `GString` into which to write the result |
426 | * |
427 | * Scans a string into a `GString` buffer. |
428 | * |
429 | * The string may either be a sequence of non-white-space characters, |
430 | * or a quoted string with '"'. Instead a quoted string, '\"' represents |
431 | * a literal quote. Leading white space outside of quotes is skipped. |
432 | * |
433 | * Return value: %FALSE if a parse error occurred |
434 | * |
435 | * Deprecated: 1.38 |
436 | */ |
437 | gboolean |
438 | pango_scan_string (const char **pos, GString *out) |
439 | { |
440 | const char *p = *pos; |
441 | |
442 | while (g_ascii_isspace (*p)) |
443 | p++; |
444 | |
445 | if (G_UNLIKELY (!*p)) |
446 | return FALSE; |
447 | else if (*p == '"') |
448 | { |
449 | gboolean quoted = FALSE; |
450 | g_string_truncate (string: out, len: 0); |
451 | |
452 | p++; |
453 | |
454 | while (TRUE) |
455 | { |
456 | if (quoted) |
457 | { |
458 | int c = *p; |
459 | |
460 | switch (c) |
461 | { |
462 | case '\0': |
463 | return FALSE; |
464 | case 'n': |
465 | c = '\n'; |
466 | break; |
467 | case 't': |
468 | c = '\t'; |
469 | break; |
470 | default: |
471 | break; |
472 | } |
473 | |
474 | quoted = FALSE; |
475 | g_string_append_c (out, c); |
476 | } |
477 | else |
478 | { |
479 | switch (*p) |
480 | { |
481 | case '\0': |
482 | return FALSE; |
483 | case '\\': |
484 | quoted = TRUE; |
485 | break; |
486 | case '"': |
487 | p++; |
488 | goto done; |
489 | default: |
490 | g_string_append_c (out, *p); |
491 | break; |
492 | } |
493 | } |
494 | p++; |
495 | } |
496 | done: |
497 | ; |
498 | } |
499 | else |
500 | { |
501 | g_string_truncate (string: out, len: 0); |
502 | |
503 | while (*p && !g_ascii_isspace (*p)) |
504 | { |
505 | g_string_append_c (out, *p); |
506 | p++; |
507 | } |
508 | } |
509 | |
510 | *pos = p; |
511 | |
512 | return TRUE; |
513 | } |
514 | |
515 | /** |
516 | * pango_scan_int: |
517 | * @pos: (inout): in/out string position |
518 | * @out: (out): an int into which to write the result |
519 | * |
520 | * Scans an integer. |
521 | * |
522 | * Leading white space is skipped. |
523 | * |
524 | * Return value: %FALSE if a parse error occurred |
525 | * |
526 | * Deprecated: 1.38 |
527 | */ |
528 | gboolean |
529 | pango_scan_int (const char **pos, int *out) |
530 | { |
531 | return _pango_scan_int (pos, out); |
532 | } |
533 | |
534 | gboolean |
535 | _pango_scan_int (const char **pos, int *out) |
536 | { |
537 | char *end; |
538 | long temp; |
539 | |
540 | errno = 0; |
541 | temp = strtol (nptr: *pos, endptr: &end, base: 10); |
542 | if (errno == ERANGE) |
543 | { |
544 | errno = 0; |
545 | return FALSE; |
546 | } |
547 | |
548 | *out = (int)temp; |
549 | if ((long)(*out) != temp) |
550 | { |
551 | return FALSE; |
552 | } |
553 | |
554 | *pos = end; |
555 | |
556 | return TRUE; |
557 | } |
558 | |
559 | /** |
560 | * pango_config_key_get_system: |
561 | * @key: Key to look up, in the form "SECTION/KEY" |
562 | * |
563 | * Do not use. Does not do anything. |
564 | * |
565 | * Return value: %NULL |
566 | * |
567 | * Deprecated: 1.38 |
568 | */ |
569 | char * |
570 | pango_config_key_get_system (const char *key) |
571 | { |
572 | return NULL; |
573 | } |
574 | |
575 | /** |
576 | * pango_config_key_get: |
577 | * @key: Key to look up, in the form "SECTION/KEY" |
578 | * |
579 | * Do not use. Does not do anything. |
580 | * |
581 | * Return value: %NULL |
582 | * |
583 | * Deprecated: 1.38 |
584 | */ |
585 | char * |
586 | pango_config_key_get (const char *key) |
587 | { |
588 | return NULL; |
589 | } |
590 | |
591 | /** |
592 | * pango_get_sysconf_subdirectory: |
593 | * |
594 | * Returns the name of the "pango" subdirectory of SYSCONFDIR |
595 | * (which is set at compile time). |
596 | * |
597 | * Return value: the Pango sysconf directory. The returned string should |
598 | * not be freed. |
599 | * |
600 | * Deprecated: 1.38 |
601 | */ |
602 | const char * |
603 | pango_get_sysconf_subdirectory (void) |
604 | { |
605 | static const gchar *result = NULL; /* MT-safe */ |
606 | |
607 | if (g_once_init_enter (&result)) |
608 | { |
609 | const char *tmp_result = NULL; |
610 | const char *sysconfdir = g_getenv (variable: "PANGO_SYSCONFDIR" ); |
611 | if (sysconfdir != NULL) |
612 | tmp_result = g_build_filename (first_element: sysconfdir, "pango" , NULL); |
613 | else |
614 | tmp_result = SYSCONFDIR "/pango" ; |
615 | g_once_init_leave(&result, tmp_result); |
616 | } |
617 | return result; |
618 | } |
619 | |
620 | /** |
621 | * pango_get_lib_subdirectory: |
622 | * |
623 | * Returns the name of the "pango" subdirectory of LIBDIR |
624 | * (which is set at compile time). |
625 | * |
626 | * Return value: the Pango lib directory. The returned string should |
627 | * not be freed. |
628 | * |
629 | * Deprecated: 1.38 |
630 | */ |
631 | const char * |
632 | pango_get_lib_subdirectory (void) |
633 | { |
634 | static const gchar *result = NULL; /* MT-safe */ |
635 | |
636 | if (g_once_init_enter (&result)) |
637 | { |
638 | const gchar *tmp_result = NULL; |
639 | const char *libdir = g_getenv (variable: "PANGO_LIBDIR" ); |
640 | if (libdir != NULL) |
641 | tmp_result = g_build_filename (first_element: libdir, "pango" , NULL); |
642 | else |
643 | tmp_result = LIBDIR "/pango" ; |
644 | g_once_init_leave(&result, tmp_result); |
645 | } |
646 | return result; |
647 | } |
648 | |
649 | |
650 | static gboolean |
651 | parse_int (const char *word, |
652 | int *out) |
653 | { |
654 | char *end; |
655 | long val; |
656 | int i; |
657 | |
658 | if (word == NULL) |
659 | return FALSE; |
660 | |
661 | val = strtol (nptr: word, endptr: &end, base: 10); |
662 | i = val; |
663 | |
664 | if (end != word && *end == '\0' && val >= 0 && val == i) |
665 | { |
666 | if (out) |
667 | *out = i; |
668 | |
669 | return TRUE; |
670 | } |
671 | |
672 | return FALSE; |
673 | } |
674 | |
675 | /** |
676 | * pango_parse_enum: |
677 | * @type: enum type to parse, eg. %PANGO_TYPE_ELLIPSIZE_MODE |
678 | * @str: (nullable): string to parse |
679 | * @value: (out) (optional): integer to store the result in |
680 | * @warn: if %TRUE, issue a g_warning() on bad input |
681 | * @possible_values: (out) (optional): place to store list of possible |
682 | * values on failure |
683 | * |
684 | * Parses an enum type and stores the result in @value. |
685 | * |
686 | * If @str does not match the nick name of any of the possible values |
687 | * for the enum and is not an integer, %FALSE is returned, a warning |
688 | * is issued if @warn is %TRUE, and a string representing the list of |
689 | * possible values is stored in @possible_values. The list is |
690 | * slash-separated, eg. "none/start/middle/end". |
691 | * |
692 | * If failed and @possible_values is not %NULL, returned string should |
693 | * be freed using g_free(). |
694 | * |
695 | * Return value: %TRUE if @str was successfully parsed |
696 | * |
697 | * Deprecated: 1.38 |
698 | * |
699 | * Since: 1.16 |
700 | */ |
701 | gboolean |
702 | pango_parse_enum (GType type, |
703 | const char *str, |
704 | int *value, |
705 | gboolean warn, |
706 | char **possible_values) |
707 | { |
708 | return _pango_parse_enum (type, str, value, warn, possible_values); |
709 | } |
710 | |
711 | gboolean |
712 | _pango_parse_enum (GType type, |
713 | const char *str, |
714 | int *value, |
715 | gboolean warn, |
716 | char **possible_values) |
717 | { |
718 | GEnumClass *class = NULL; |
719 | gboolean ret = TRUE; |
720 | GEnumValue *v = NULL; |
721 | |
722 | class = g_type_class_ref (type); |
723 | |
724 | if (G_LIKELY (str)) |
725 | v = g_enum_get_value_by_nick (enum_class: class, nick: str); |
726 | |
727 | if (v) |
728 | { |
729 | if (G_LIKELY (value)) |
730 | *value = v->value; |
731 | } |
732 | else if (!parse_int (word: str, out: value)) |
733 | { |
734 | ret = FALSE; |
735 | if (G_LIKELY (warn || possible_values)) |
736 | { |
737 | int i; |
738 | GString *s = g_string_new (NULL); |
739 | |
740 | for (i = 0, v = g_enum_get_value (enum_class: class, value: i); v; |
741 | i++ , v = g_enum_get_value (enum_class: class, value: i)) |
742 | { |
743 | if (i) |
744 | g_string_append_c (s, '/'); |
745 | g_string_append (string: s, val: v->value_nick); |
746 | } |
747 | |
748 | if (warn) |
749 | g_warning ("%s must be one of %s" , |
750 | G_ENUM_CLASS_TYPE_NAME(class), |
751 | s->str); |
752 | |
753 | if (possible_values) |
754 | *possible_values = s->str; |
755 | |
756 | g_string_free (string: s, free_segment: possible_values ? FALSE : TRUE); |
757 | } |
758 | } |
759 | |
760 | g_type_class_unref (g_class: class); |
761 | |
762 | return ret; |
763 | } |
764 | |
765 | gboolean |
766 | pango_parse_flags (GType type, |
767 | const char *str, |
768 | int *value, |
769 | char **possible_values) |
770 | { |
771 | GFlagsClass *class = NULL; |
772 | gboolean ret = TRUE; |
773 | GFlagsValue *v = NULL; |
774 | |
775 | class = g_type_class_ref (type); |
776 | |
777 | v = g_flags_get_value_by_nick (flags_class: class, nick: str); |
778 | |
779 | if (v) |
780 | { |
781 | *value = v->value; |
782 | } |
783 | else if (!parse_int (word: str, out: value)) |
784 | { |
785 | char **strv = g_strsplit (string: str, delimiter: "|" , max_tokens: 0); |
786 | int i; |
787 | |
788 | *value = 0; |
789 | |
790 | for (i = 0; strv[i]; i++) |
791 | { |
792 | strv[i] = g_strstrip (strv[i]); |
793 | v = g_flags_get_value_by_nick (flags_class: class, nick: strv[i]); |
794 | if (!v) |
795 | { |
796 | ret = FALSE; |
797 | break; |
798 | } |
799 | *value |= v->value; |
800 | } |
801 | g_strfreev (str_array: strv); |
802 | |
803 | if (!ret && possible_values) |
804 | { |
805 | int i; |
806 | GString *s = g_string_new (NULL); |
807 | |
808 | for (i = 0; i < class->n_values; i++) |
809 | { |
810 | v = &class->values[i]; |
811 | if (i) |
812 | g_string_append_c (s, '/'); |
813 | g_string_append (string: s, val: v->value_nick); |
814 | } |
815 | |
816 | *possible_values = s->str; |
817 | |
818 | g_string_free (string: s, FALSE); |
819 | } |
820 | } |
821 | |
822 | g_type_class_unref (g_class: class); |
823 | |
824 | return ret; |
825 | } |
826 | |
827 | /** |
828 | * pango_lookup_aliases: |
829 | * @fontname: an ASCII string |
830 | * @families: (out) (array length=n_families): will be set to an array of |
831 | * font family names. This array is owned by Pango and should not be freed |
832 | * @n_families: (out): will be set to the length of the @families array |
833 | * |
834 | * Look up all user defined aliases for the alias @fontname. |
835 | * |
836 | * The resulting font family names will be stored in @families, |
837 | * and the number of families in @n_families. |
838 | * |
839 | * Deprecated: 1.32: This function is not thread-safe. |
840 | */ |
841 | void |
842 | pango_lookup_aliases (const char *fontname, |
843 | char ***families, |
844 | int *n_families) |
845 | { |
846 | *families = NULL; |
847 | *n_families = 0; |
848 | } |
849 | |
850 | #pragma GCC diagnostic push |
851 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations" |
852 | |
853 | /** |
854 | * pango_find_base_dir: |
855 | * @text: the text to process. Must be valid UTF-8 |
856 | * @length: length of @text in bytes (may be -1 if @text is nul-terminated) |
857 | * |
858 | * Searches a string the first character that has a strong |
859 | * direction, according to the Unicode bidirectional algorithm. |
860 | * |
861 | * Return value: The direction corresponding to the first strong character. |
862 | * If no such character is found, then %PANGO_DIRECTION_NEUTRAL is returned. |
863 | * |
864 | * Since: 1.4 |
865 | */ |
866 | PangoDirection |
867 | pango_find_base_dir (const gchar *text, |
868 | gint length) |
869 | { |
870 | PangoDirection dir = PANGO_DIRECTION_NEUTRAL; |
871 | const gchar *p; |
872 | |
873 | g_return_val_if_fail (text != NULL || length == 0, PANGO_DIRECTION_NEUTRAL); |
874 | |
875 | p = text; |
876 | while ((length < 0 || p < text + length) && *p) |
877 | { |
878 | gunichar wc = g_utf8_get_char (p); |
879 | |
880 | dir = pango_unichar_direction (ch: wc); |
881 | |
882 | if (dir != PANGO_DIRECTION_NEUTRAL) |
883 | break; |
884 | |
885 | p = g_utf8_next_char (p); |
886 | } |
887 | |
888 | return dir; |
889 | } |
890 | |
891 | #pragma GCC diagnostic pop |
892 | |
893 | /** |
894 | * pango_is_zero_width: |
895 | * @ch: a Unicode character |
896 | * |
897 | * Checks if a character that should not be normally rendered. |
898 | * |
899 | * This includes all Unicode characters with "ZERO WIDTH" in their name, |
900 | * as well as *bidi* formatting characters, and a few other ones. |
901 | * |
902 | * This is totally different from [func@GLib.unichar_iszerowidth] and is at best misnamed. |
903 | * |
904 | * Return value: %TRUE if @ch is a zero-width character, %FALSE otherwise |
905 | * |
906 | * Since: 1.10 |
907 | */ |
908 | gboolean |
909 | pango_is_zero_width (gunichar ch) |
910 | { |
911 | /* Zero Width characters: |
912 | * |
913 | * 00AD SOFT HYPHEN |
914 | * 034F COMBINING GRAPHEME JOINER |
915 | * |
916 | * 200B ZERO WIDTH SPACE |
917 | * 200C ZERO WIDTH NON-JOINER |
918 | * 200D ZERO WIDTH JOINER |
919 | * 200E LEFT-TO-RIGHT MARK |
920 | * 200F RIGHT-TO-LEFT MARK |
921 | * |
922 | * 2028 LINE SEPARATOR |
923 | * |
924 | * 2060 WORD JOINER |
925 | * 2061 FUNCTION APPLICATION |
926 | * 2062 INVISIBLE TIMES |
927 | * 2063 INVISIBLE SEPARATOR |
928 | * |
929 | * 2066 LEFT-TO-RIGHT ISOLATE |
930 | * 2067 RIGHT-TO-LEFT ISOLATE |
931 | * 2068 FIRST STRONG ISOLATE |
932 | * 2069 POP DIRECTIONAL ISOLATE |
933 | * |
934 | * 202A LEFT-TO-RIGHT EMBEDDING |
935 | * 202B RIGHT-TO-LEFT EMBEDDING |
936 | * 202C POP DIRECTIONAL FORMATTING |
937 | * 202D LEFT-TO-RIGHT OVERRIDE |
938 | * 202E RIGHT-TO-LEFT OVERRIDE |
939 | * |
940 | * FEFF ZERO WIDTH NO-BREAK SPACE |
941 | */ |
942 | return ((ch & ~(gunichar)0x007F) == 0x2000 && ( |
943 | (ch >= 0x200B && ch <= 0x200F) || |
944 | (ch >= 0x202A && ch <= 0x202E) || |
945 | (ch >= 0x2060 && ch <= 0x2063) || |
946 | (ch >= 0x2066 && ch <= 0x2069) || |
947 | (ch == 0x2028) |
948 | )) || G_UNLIKELY (ch == 0x00AD |
949 | || ch == 0x034F |
950 | || ch == 0xFEFF); |
951 | } |
952 | |
953 | /** |
954 | * pango_quantize_line_geometry: |
955 | * @thickness: (inout): pointer to the thickness of a line, in Pango units |
956 | * @position: (inout): corresponding position |
957 | * |
958 | * Quantizes the thickness and position of a line to whole device pixels. |
959 | * |
960 | * This is typically used for underline or strikethrough. The purpose of |
961 | * this function is to avoid such lines looking blurry. |
962 | * |
963 | * Care is taken to make sure @thickness is at least one pixel when this |
964 | * function returns, but returned @position may become zero as a result |
965 | * of rounding. |
966 | * |
967 | * Since: 1.12 |
968 | */ |
969 | void |
970 | pango_quantize_line_geometry (int *thickness, |
971 | int *position) |
972 | { |
973 | int thickness_pixels = (*thickness + PANGO_SCALE / 2) / PANGO_SCALE; |
974 | if (thickness_pixels == 0) |
975 | thickness_pixels = 1; |
976 | |
977 | if (thickness_pixels & 1) |
978 | { |
979 | int new_center = ((*position - *thickness / 2) & ~(PANGO_SCALE - 1)) + PANGO_SCALE / 2; |
980 | *position = new_center + (PANGO_SCALE * thickness_pixels) / 2; |
981 | } |
982 | else |
983 | { |
984 | int new_center = ((*position - *thickness / 2 + PANGO_SCALE / 2) & ~(PANGO_SCALE - 1)); |
985 | *position = new_center + (PANGO_SCALE * thickness_pixels) / 2; |
986 | } |
987 | |
988 | *thickness = thickness_pixels * PANGO_SCALE; |
989 | } |
990 | |
991 | /** |
992 | * pango_units_from_double: |
993 | * @d: double floating-point value |
994 | * |
995 | * Converts a floating-point number to Pango units. |
996 | * |
997 | * The conversion is done by multiplying @d by %PANGO_SCALE and |
998 | * rounding the result to nearest integer. |
999 | * |
1000 | * Return value: the value in Pango units. |
1001 | * |
1002 | * Since: 1.16 |
1003 | */ |
1004 | int |
1005 | pango_units_from_double (double d) |
1006 | { |
1007 | return (int)floor (x: d * PANGO_SCALE + 0.5); |
1008 | } |
1009 | |
1010 | /** |
1011 | * pango_units_to_double: |
1012 | * @i: value in Pango units |
1013 | * |
1014 | * Converts a number in Pango units to floating-point. |
1015 | * |
1016 | * The conversion is done by dividing @i by %PANGO_SCALE. |
1017 | * |
1018 | * Return value: the double value. |
1019 | * |
1020 | * Since: 1.16 |
1021 | */ |
1022 | double |
1023 | pango_units_to_double (int i) |
1024 | { |
1025 | return (double)i / PANGO_SCALE; |
1026 | } |
1027 | |
1028 | /** |
1029 | * pango_extents_to_pixels: |
1030 | * @inclusive: (nullable): rectangle to round to pixels inclusively |
1031 | * @nearest: (nullable): rectangle to round to nearest pixels |
1032 | * |
1033 | * Converts extents from Pango units to device units. |
1034 | * |
1035 | * The conversion is done by dividing by the %PANGO_SCALE factor and |
1036 | * performing rounding. |
1037 | * |
1038 | * The @inclusive rectangle is converted by flooring the x/y coordinates |
1039 | * and extending width/height, such that the final rectangle completely |
1040 | * includes the original rectangle. |
1041 | * |
1042 | * The @nearest rectangle is converted by rounding the coordinates |
1043 | * of the rectangle to the nearest device unit (pixel). |
1044 | * |
1045 | * The rule to which argument to use is: if you want the resulting device-space |
1046 | * rectangle to completely contain the original rectangle, pass it in as |
1047 | * @inclusive. If you want two touching-but-not-overlapping rectangles stay |
1048 | * touching-but-not-overlapping after rounding to device units, pass them in |
1049 | * as @nearest. |
1050 | * |
1051 | * Since: 1.16 |
1052 | */ |
1053 | void |
1054 | pango_extents_to_pixels (PangoRectangle *inclusive, |
1055 | PangoRectangle *nearest) |
1056 | { |
1057 | if (inclusive) |
1058 | { |
1059 | int orig_x = inclusive->x; |
1060 | int orig_y = inclusive->y; |
1061 | |
1062 | inclusive->x = PANGO_PIXELS_FLOOR (inclusive->x); |
1063 | inclusive->y = PANGO_PIXELS_FLOOR (inclusive->y); |
1064 | |
1065 | inclusive->width = PANGO_PIXELS_CEIL (orig_x + inclusive->width ) - inclusive->x; |
1066 | inclusive->height = PANGO_PIXELS_CEIL (orig_y + inclusive->height) - inclusive->y; |
1067 | } |
1068 | |
1069 | if (nearest) |
1070 | { |
1071 | int orig_x = nearest->x; |
1072 | int orig_y = nearest->y; |
1073 | |
1074 | nearest->x = PANGO_PIXELS (nearest->x); |
1075 | nearest->y = PANGO_PIXELS (nearest->y); |
1076 | |
1077 | nearest->width = PANGO_PIXELS (orig_x + nearest->width ) - nearest->x; |
1078 | nearest->height = PANGO_PIXELS (orig_y + nearest->height) - nearest->y; |
1079 | } |
1080 | } |
1081 | |
1082 | |
1083 | |
1084 | |
1085 | |
1086 | /********************************************************* |
1087 | * Some internal functions for handling PANGO_ATTR_SHAPE * |
1088 | ********************************************************/ |
1089 | |
1090 | void |
1091 | _pango_shape_shape (const char *text, |
1092 | unsigned int n_chars, |
1093 | PangoRectangle *shape_ink G_GNUC_UNUSED, |
1094 | PangoRectangle *shape_logical, |
1095 | PangoGlyphString *glyphs) |
1096 | { |
1097 | unsigned int i; |
1098 | const char *p; |
1099 | |
1100 | pango_glyph_string_set_size (string: glyphs, new_len: n_chars); |
1101 | |
1102 | for (i=0, p = text; i < n_chars; i++, p = g_utf8_next_char (p)) |
1103 | { |
1104 | glyphs->glyphs[i].glyph = PANGO_GLYPH_EMPTY; |
1105 | glyphs->glyphs[i].geometry.x_offset = 0; |
1106 | glyphs->glyphs[i].geometry.y_offset = 0; |
1107 | glyphs->glyphs[i].geometry.width = shape_logical->width; |
1108 | glyphs->glyphs[i].attr.is_cluster_start = 1; |
1109 | |
1110 | glyphs->log_clusters[i] = p - text; |
1111 | } |
1112 | } |
1113 | |
1114 | void |
1115 | _pango_shape_get_extents (gint n_chars, |
1116 | PangoRectangle *shape_ink, |
1117 | PangoRectangle *shape_logical, |
1118 | PangoRectangle *ink_rect, |
1119 | PangoRectangle *logical_rect) |
1120 | { |
1121 | if (n_chars > 0) |
1122 | { |
1123 | if (ink_rect) |
1124 | { |
1125 | ink_rect->x = MIN (shape_ink->x, shape_ink->x + shape_logical->width * (n_chars - 1)); |
1126 | ink_rect->width = MAX (shape_ink->width, shape_ink->width + shape_logical->width * (n_chars - 1)); |
1127 | ink_rect->y = shape_ink->y; |
1128 | ink_rect->height = shape_ink->height; |
1129 | } |
1130 | if (logical_rect) |
1131 | { |
1132 | logical_rect->x = MIN (shape_logical->x, shape_logical->x + shape_logical->width * (n_chars - 1)); |
1133 | logical_rect->width = MAX (shape_logical->width, shape_logical->width + shape_logical->width * (n_chars - 1)); |
1134 | logical_rect->y = shape_logical->y; |
1135 | logical_rect->height = shape_logical->height; |
1136 | } |
1137 | } |
1138 | else |
1139 | { |
1140 | if (ink_rect) |
1141 | { |
1142 | ink_rect->x = 0; |
1143 | ink_rect->y = 0; |
1144 | ink_rect->width = 0; |
1145 | ink_rect->height = 0; |
1146 | } |
1147 | |
1148 | if (logical_rect) |
1149 | { |
1150 | logical_rect->x = 0; |
1151 | logical_rect->y = 0; |
1152 | logical_rect->width = 0; |
1153 | logical_rect->height = 0; |
1154 | } |
1155 | } |
1156 | } |
1157 | |
1158 | /** |
1159 | * pango_find_paragraph_boundary: |
1160 | * @text: UTF-8 text |
1161 | * @length: length of @text in bytes, or -1 if nul-terminated |
1162 | * @paragraph_delimiter_index: (out): return location for index of |
1163 | * delimiter |
1164 | * @next_paragraph_start: (out): return location for start of next |
1165 | * paragraph |
1166 | * |
1167 | * Locates a paragraph boundary in @text. |
1168 | * |
1169 | * A boundary is caused by delimiter characters, such as |
1170 | * a newline, carriage return, carriage return-newline pair, |
1171 | * or Unicode paragraph separator character. |
1172 | * |
1173 | * The index of the run of delimiters is returned in |
1174 | * @paragraph_delimiter_index. The index of the start of the |
1175 | * next paragraph (index after all delimiters) is stored n |
1176 | * @next_paragraph_start. |
1177 | * |
1178 | * If no delimiters are found, both @paragraph_delimiter_index |
1179 | * and @next_paragraph_start are filled with the length of @text |
1180 | * (an index one off the end). |
1181 | */ |
1182 | void |
1183 | pango_find_paragraph_boundary (const char *text, |
1184 | int length, |
1185 | int *paragraph_delimiter_index, |
1186 | int *next_paragraph_start) |
1187 | { |
1188 | const char *p = text; |
1189 | const char *end; |
1190 | const char *start = NULL; |
1191 | const char *delimiter = NULL; |
1192 | |
1193 | /* Only one character has type G_UNICODE_PARAGRAPH_SEPARATOR in |
1194 | * Unicode 5.0; update the following code if that changes. |
1195 | */ |
1196 | |
1197 | /* prev_sep is the first byte of the previous separator. Since |
1198 | * the valid separators are \r, \n, and PARAGRAPH_SEPARATOR, the |
1199 | * first byte is enough to identify it. |
1200 | */ |
1201 | char prev_sep; |
1202 | |
1203 | #define PARAGRAPH_SEPARATOR_STRING "\xE2\x80\xA9" |
1204 | |
1205 | if (length < 0) |
1206 | length = strlen (s: text); |
1207 | |
1208 | end = text + length; |
1209 | |
1210 | if (paragraph_delimiter_index) |
1211 | *paragraph_delimiter_index = length; |
1212 | |
1213 | if (next_paragraph_start) |
1214 | *next_paragraph_start = length; |
1215 | |
1216 | if (length == 0) |
1217 | return; |
1218 | |
1219 | prev_sep = 0; |
1220 | while (p < end) |
1221 | { |
1222 | if (prev_sep == '\n' || |
1223 | prev_sep == PARAGRAPH_SEPARATOR_STRING[0]) |
1224 | { |
1225 | g_assert (delimiter); |
1226 | start = p; |
1227 | break; |
1228 | } |
1229 | else if (prev_sep == '\r') |
1230 | { |
1231 | /* don't break between \r and \n */ |
1232 | if (*p != '\n') |
1233 | { |
1234 | g_assert (delimiter); |
1235 | start = p; |
1236 | break; |
1237 | } |
1238 | } |
1239 | |
1240 | if (*p == '\n' || |
1241 | *p == '\r' || |
1242 | !strncmp(s1: p, PARAGRAPH_SEPARATOR_STRING, n: strlen (PARAGRAPH_SEPARATOR_STRING))) |
1243 | { |
1244 | if (delimiter == NULL) |
1245 | delimiter = p; |
1246 | prev_sep = *p; |
1247 | } |
1248 | else |
1249 | prev_sep = 0; |
1250 | |
1251 | p = g_utf8_next_char (p); |
1252 | } |
1253 | |
1254 | if (delimiter && paragraph_delimiter_index) |
1255 | *paragraph_delimiter_index = delimiter - text; |
1256 | |
1257 | if (start && next_paragraph_start) |
1258 | *next_paragraph_start = start - text; |
1259 | } |
1260 | |