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
pango_version(void)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 *
pango_version_string(void)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*
pango_version_check(int required_major,int required_minor,int required_micro)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 *
pango_trim_string(const char * str)152 pango_trim_string (const char *str)
153 {
154 return _pango_trim_string (str);
155 }
156
157 char *
_pango_trim_string(const char * str)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 (str);
168 while (len > 0 && g_ascii_isspace (str[len-1]))
169 len--;
170
171 return g_strndup (str, 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 **
pango_split_file_list(const char * str)187 pango_split_file_list (const char *str)
188 {
189 int i = 0;
190 int j;
191 char **files;
192
193 files = g_strsplit (str, G_SEARCHPATH_SEPARATOR_S, -1);
194
195 while (files[i])
196 {
197 char *file = _pango_trim_string (files[i]);
198
199 /* If the resulting file is empty, skip it */
200 if (file[0] == '\0')
201 {
202 g_free(file);
203 g_free (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 (g_get_home_dir(), file + 1, NULL);
222 g_free (file);
223 file = tmp;
224 }
225 else if (file[0] == '~' && file[1] == '\0')
226 {
227 g_free (file);
228 file = g_strdup (g_get_home_dir());
229 }
230 #endif
231 g_free (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
pango_read_line(FILE * stream,GString * str)261 pango_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);
269
270 g_string_truncate (str, 0);
271
272 while (1)
273 {
274 int c;
275
276 c = getc_unlocked (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 (stream);
301
302 if (!(next_c == EOF ||
303 (c == '\r' && next_c == '\n') ||
304 (c == '\n' && next_c == '\r')))
305 ungetc (next_c, 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 (stream);
331
332 if (!(next_c == EOF ||
333 (c == '\r' && next_c == '\n') ||
334 (c == '\n' && next_c == '\r')))
335 ungetc (next_c, 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);
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
pango_skip_space(const char ** pos)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
pango_scan_word(const char ** pos,GString * out)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 (out, 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
pango_scan_string(const char ** pos,GString * out)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 (out, 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 (out, 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
pango_scan_int(const char ** pos,int * out)529 pango_scan_int (const char **pos, int *out)
530 {
531 return _pango_scan_int (pos, out);
532 }
533
534 gboolean
_pango_scan_int(const char ** pos,int * out)535 _pango_scan_int (const char **pos, int *out)
536 {
537 char *end;
538 long temp;
539
540 errno = 0;
541 temp = strtol (*pos, &end, 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 *
pango_config_key_get_system(const char * key)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 *
pango_config_key_get(const char * key)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 *
pango_get_sysconf_subdirectory(void)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 ("PANGO_SYSCONFDIR");
611 if (sysconfdir != NULL)
612 tmp_result = g_build_filename (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 *
pango_get_lib_subdirectory(void)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 ("PANGO_LIBDIR");
640 if (libdir != NULL)
641 tmp_result = g_build_filename (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
parse_int(const char * word,int * out)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 (word, &end, 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
pango_parse_enum(GType type,const char * str,int * value,gboolean warn,char ** possible_values)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
_pango_parse_enum(GType type,const char * str,int * value,gboolean warn,char ** possible_values)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 (class, str);
726
727 if (v)
728 {
729 if (G_LIKELY (value))
730 *value = v->value;
731 }
732 else if (!parse_int (str, 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 (class, i); v;
741 i++ , v = g_enum_get_value (class, i))
742 {
743 if (i)
744 g_string_append_c (s, '/');
745 g_string_append (s, 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 (s, possible_values ? FALSE : TRUE);
757 }
758 }
759
760 g_type_class_unref (class);
761
762 return ret;
763 }
764
765 gboolean
pango_parse_flags(GType type,const char * str,int * value,char ** possible_values)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 (class, str);
778
779 if (v)
780 {
781 *value = v->value;
782 }
783 else if (!parse_int (str, value))
784 {
785 char **strv = g_strsplit (str, "|", 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 (class, strv[i]);
794 if (!v)
795 {
796 ret = FALSE;
797 break;
798 }
799 *value |= v->value;
800 }
801 g_strfreev (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 (s, v->value_nick);
814 }
815
816 *possible_values = s->str;
817
818 g_string_free (s, FALSE);
819 }
820 }
821
822 g_type_class_unref (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
pango_lookup_aliases(const char * fontname,char *** families,int * n_families)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
pango_find_base_dir(const gchar * text,gint length)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 (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. This is
901 * totally different from g_unichar_iszerowidth() and is at best misnamed.
902 *
903 * Return value: %TRUE if @ch is a zero-width character, %FALSE otherwise
904 *
905 * Since: 1.10
906 */
907 gboolean
pango_is_zero_width(gunichar ch)908 pango_is_zero_width (gunichar ch)
909 {
910 /* Zero Width characters:
911 *
912 * 00AD SOFT HYPHEN
913 * 034F COMBINING GRAPHEME JOINER
914 *
915 * 200B ZERO WIDTH SPACE
916 * 200C ZERO WIDTH NON-JOINER
917 * 200D ZERO WIDTH JOINER
918 * 200E LEFT-TO-RIGHT MARK
919 * 200F RIGHT-TO-LEFT MARK
920 *
921 * 2028 LINE SEPARATOR
922 *
923 * 2060 WORD JOINER
924 * 2061 FUNCTION APPLICATION
925 * 2062 INVISIBLE TIMES
926 * 2063 INVISIBLE SEPARATOR
927 *
928 * 2066 LEFT-TO-RIGHT ISOLATE
929 * 2067 RIGHT-TO-LEFT ISOLATE
930 * 2068 FIRST STRONG ISOLATE
931 * 2069 POP DIRECTIONAL ISOLATE
932 *
933 * 202A LEFT-TO-RIGHT EMBEDDING
934 * 202B RIGHT-TO-LEFT EMBEDDING
935 * 202C POP DIRECTIONAL FORMATTING
936 * 202D LEFT-TO-RIGHT OVERRIDE
937 * 202E RIGHT-TO-LEFT OVERRIDE
938 *
939 * FEFF ZERO WIDTH NO-BREAK SPACE
940 */
941 return ((ch & ~(gunichar)0x007F) == 0x2000 && (
942 (ch >= 0x200B && ch <= 0x200F) ||
943 (ch >= 0x202A && ch <= 0x202E) ||
944 (ch >= 0x2060 && ch <= 0x2063) ||
945 (ch >= 0x2066 && ch <= 0x2069) ||
946 (ch == 0x2028)
947 )) || G_UNLIKELY (ch == 0x00AD
948 || ch == 0x034F
949 || ch == 0xFEFF);
950 }
951
952 /**
953 * pango_quantize_line_geometry:
954 * @thickness: (inout): pointer to the thickness of a line, in Pango units
955 * @position: (inout): corresponding position
956 *
957 * Quantizes the thickness and position of a line to whole device pixels.
958 *
959 * This is typically used for underline or strikethrough. The purpose of
960 * this function is to avoid such lines looking blurry.
961 *
962 * Care is taken to make sure @thickness is at least one pixel when this
963 * function returns, but returned @position may become zero as a result
964 * of rounding.
965 *
966 * Since: 1.12
967 */
968 void
pango_quantize_line_geometry(int * thickness,int * position)969 pango_quantize_line_geometry (int *thickness,
970 int *position)
971 {
972 int thickness_pixels = (*thickness + PANGO_SCALE / 2) / PANGO_SCALE;
973 if (thickness_pixels == 0)
974 thickness_pixels = 1;
975
976 if (thickness_pixels & 1)
977 {
978 int new_center = ((*position - *thickness / 2) & ~(PANGO_SCALE - 1)) + PANGO_SCALE / 2;
979 *position = new_center + (PANGO_SCALE * thickness_pixels) / 2;
980 }
981 else
982 {
983 int new_center = ((*position - *thickness / 2 + PANGO_SCALE / 2) & ~(PANGO_SCALE - 1));
984 *position = new_center + (PANGO_SCALE * thickness_pixels) / 2;
985 }
986
987 *thickness = thickness_pixels * PANGO_SCALE;
988 }
989
990 /**
991 * pango_units_from_double:
992 * @d: double floating-point value
993 *
994 * Converts a floating-point number to Pango units.
995 *
996 * The conversion is done by multiplying @d by %PANGO_SCALE and
997 * rounding the result to nearest integer.
998 *
999 * Return value: the value in Pango units.
1000 *
1001 * Since: 1.16
1002 */
1003 int
pango_units_from_double(double d)1004 pango_units_from_double (double d)
1005 {
1006 return (int)floor (d * PANGO_SCALE + 0.5);
1007 }
1008
1009 /**
1010 * pango_units_to_double:
1011 * @i: value in Pango units
1012 *
1013 * Converts a number in Pango units to floating-point.
1014 *
1015 * The conversion is done by dividing @i by %PANGO_SCALE.
1016 *
1017 * Return value: the double value.
1018 *
1019 * Since: 1.16
1020 */
1021 double
pango_units_to_double(int i)1022 pango_units_to_double (int i)
1023 {
1024 return (double)i / PANGO_SCALE;
1025 }
1026
1027 /**
1028 * pango_extents_to_pixels:
1029 * @inclusive: (nullable): rectangle to round to pixels inclusively
1030 * @nearest: (nullable): rectangle to round to nearest pixels
1031 *
1032 * Converts extents from Pango units to device units.
1033 *
1034 * The conversion is done by dividing by the %PANGO_SCALE factor and
1035 * performing rounding.
1036 *
1037 * The @inclusive rectangle is converted by flooring the x/y coordinates
1038 * and extending width/height, such that the final rectangle completely
1039 * includes the original rectangle.
1040 *
1041 * The @nearest rectangle is converted by rounding the coordinates
1042 * of the rectangle to the nearest device unit (pixel).
1043 *
1044 * The rule to which argument to use is: if you want the resulting device-space
1045 * rectangle to completely contain the original rectangle, pass it in as
1046 * @inclusive. If you want two touching-but-not-overlapping rectangles stay
1047 * touching-but-not-overlapping after rounding to device units, pass them in
1048 * as @nearest.
1049 *
1050 * Since: 1.16
1051 */
1052 void
pango_extents_to_pixels(PangoRectangle * inclusive,PangoRectangle * nearest)1053 pango_extents_to_pixels (PangoRectangle *inclusive,
1054 PangoRectangle *nearest)
1055 {
1056 if (inclusive)
1057 {
1058 int orig_x = inclusive->x;
1059 int orig_y = inclusive->y;
1060
1061 inclusive->x = PANGO_PIXELS_FLOOR (inclusive->x);
1062 inclusive->y = PANGO_PIXELS_FLOOR (inclusive->y);
1063
1064 inclusive->width = PANGO_PIXELS_CEIL (orig_x + inclusive->width ) - inclusive->x;
1065 inclusive->height = PANGO_PIXELS_CEIL (orig_y + inclusive->height) - inclusive->y;
1066 }
1067
1068 if (nearest)
1069 {
1070 int orig_x = nearest->x;
1071 int orig_y = nearest->y;
1072
1073 nearest->x = PANGO_PIXELS (nearest->x);
1074 nearest->y = PANGO_PIXELS (nearest->y);
1075
1076 nearest->width = PANGO_PIXELS (orig_x + nearest->width ) - nearest->x;
1077 nearest->height = PANGO_PIXELS (orig_y + nearest->height) - nearest->y;
1078 }
1079 }
1080
1081
1082
1083
1084
1085 /*********************************************************
1086 * Some internal functions for handling PANGO_ATTR_SHAPE *
1087 ********************************************************/
1088
1089 void
_pango_shape_shape(const char * text,unsigned int n_chars,PangoRectangle * shape_ink G_GNUC_UNUSED,PangoRectangle * shape_logical,PangoGlyphString * glyphs)1090 _pango_shape_shape (const char *text,
1091 unsigned int n_chars,
1092 PangoRectangle *shape_ink G_GNUC_UNUSED,
1093 PangoRectangle *shape_logical,
1094 PangoGlyphString *glyphs)
1095 {
1096 unsigned int i;
1097 const char *p;
1098
1099 pango_glyph_string_set_size (glyphs, n_chars);
1100
1101 for (i=0, p = text; i < n_chars; i++, p = g_utf8_next_char (p))
1102 {
1103 glyphs->glyphs[i].glyph = PANGO_GLYPH_EMPTY;
1104 glyphs->glyphs[i].geometry.x_offset = 0;
1105 glyphs->glyphs[i].geometry.y_offset = 0;
1106 glyphs->glyphs[i].geometry.width = shape_logical->width;
1107 glyphs->glyphs[i].attr.is_cluster_start = 1;
1108
1109 glyphs->log_clusters[i] = p - text;
1110 }
1111 }
1112
1113 void
_pango_shape_get_extents(gint n_chars,PangoRectangle * shape_ink,PangoRectangle * shape_logical,PangoRectangle * ink_rect,PangoRectangle * logical_rect)1114 _pango_shape_get_extents (gint n_chars,
1115 PangoRectangle *shape_ink,
1116 PangoRectangle *shape_logical,
1117 PangoRectangle *ink_rect,
1118 PangoRectangle *logical_rect)
1119 {
1120 if (n_chars > 0)
1121 {
1122 if (ink_rect)
1123 {
1124 ink_rect->x = MIN (shape_ink->x, shape_ink->x + shape_logical->width * (n_chars - 1));
1125 ink_rect->width = MAX (shape_ink->width, shape_ink->width + shape_logical->width * (n_chars - 1));
1126 ink_rect->y = shape_ink->y;
1127 ink_rect->height = shape_ink->height;
1128 }
1129 if (logical_rect)
1130 {
1131 logical_rect->x = MIN (shape_logical->x, shape_logical->x + shape_logical->width * (n_chars - 1));
1132 logical_rect->width = MAX (shape_logical->width, shape_logical->width + shape_logical->width * (n_chars - 1));
1133 logical_rect->y = shape_logical->y;
1134 logical_rect->height = shape_logical->height;
1135 }
1136 }
1137 else
1138 {
1139 if (ink_rect)
1140 {
1141 ink_rect->x = 0;
1142 ink_rect->y = 0;
1143 ink_rect->width = 0;
1144 ink_rect->height = 0;
1145 }
1146
1147 if (logical_rect)
1148 {
1149 logical_rect->x = 0;
1150 logical_rect->y = 0;
1151 logical_rect->width = 0;
1152 logical_rect->height = 0;
1153 }
1154 }
1155 }
1156
1157