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 */
65int
66pango_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 */
85const char *
86pango_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 */
122const gchar*
123pango_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 */
151char *
152pango_trim_string (const char *str)
153{
154 return _pango_trim_string (str);
155}
156
157char *
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 */
186char **
187pango_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 */
260gint
261pango_read_line (FILE *stream, GString *str)
262{
263 gboolean quoted = FALSE;
264 gboolean comment = 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 */
364gboolean
365pango_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 */
391gboolean
392pango_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 */
437gboolean
438pango_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 */
528gboolean
529pango_scan_int (const char **pos, int *out)
530{
531 return _pango_scan_int (pos, out);
532}
533
534gboolean
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 */
569char *
570pango_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 */
585char *
586pango_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 */
602const char *
603pango_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 */
631const char *
632pango_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
650static gboolean
651parse_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 */
701gboolean
702pango_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
711gboolean
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
765gboolean
766pango_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 */
841void
842pango_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 */
866PangoDirection
867pango_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 */
908gboolean
909pango_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 */
969void
970pango_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 */
1004int
1005pango_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 */
1022double
1023pango_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 */
1053void
1054pango_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
1090void
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
1114void
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 */
1182void
1183pango_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

source code of gtk/subprojects/pango/pango/pango-utils.c