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