pango-utils.c 28.6 KB
Newer Older
1
/* Pango
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 * pango-utils.c: Utilities for internal functions and modules
 *
 * Copyright (C) 2000 Red Hat Software
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

22
#include "config.h"
23
24
#include <errno.h>
#include <string.h>
Owen Taylor's avatar
Owen Taylor committed
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"
Matthias Clasen's avatar
Matthias Clasen committed
32
#include "pango-utils-internal.h"
Matthias Clasen's avatar
Matthias Clasen committed
33
#include "pango-utils-private.h"
34

35
36
#include <glib/gstdio.h>

37
38
39
40
41
42
#ifndef HAVE_FLOCKFILE
#  define flockfile(f) (void)1
#  define funlockfile(f) (void)1
#  define getc_unlocked(f) getc(f)
#endif /* !HAVE_FLOCKFILE */

43
44
#ifdef G_OS_WIN32

Tor Lillqvist's avatar
Tor Lillqvist committed
45
46
#include <sys/types.h>

47
48
49
50
51
#define STRICT
#include <windows.h>

#endif

52
53
54
/**
 * pango_version:
 *
Matthias Clasen's avatar
Matthias Clasen committed
55
 * Returns the encoded version of Pango available at run-time.
56
 *
Matthias Clasen's avatar
Matthias Clasen committed
57
58
59
 * This is similar to the macro %PANGO_VERSION except that the macro
 * returns the encoded version available at compile-time. A version
 * number can be encoded into an integer using PANGO_VERSION_ENCODE().
60
 *
Matthias Clasen's avatar
Matthias Clasen committed
61
 * Returns: The encoded version of Pango library available at run time.
62
63
 *
 * Since: 1.16
64
 */
65
66
67
68
69
70
71
72
73
int
pango_version (void)
{
  return PANGO_VERSION;
}

/**
 * pango_version_string:
 *
Matthias Clasen's avatar
Matthias Clasen committed
74
75
76
77
 * Returns the version of Pango available at run-time.
 *
 * This is similar to the macro %PANGO_VERSION_STRING except that the
 * macro returns the version available at compile-time.
78
 *
Matthias Clasen's avatar
Matthias Clasen committed
79
80
81
 * Returns: A string containing the version of Pango library available
 *   at run time. The returned string is owned by Pango and should not
 *   be modified or freed.
82
83
 *
 * Since: 1.16
84
 */
85
86
87
88
89
90
91
92
const char *
pango_version_string (void)
{
  return PANGO_VERSION_STRING;
}

/**
 * pango_version_check:
93
94
95
 * @required_major: the required major version
 * @required_minor: the required minor version
 * @required_micro: the required major version
96
97
 *
 * Checks that the Pango library in use is compatible with the
Matthias Clasen's avatar
Matthias Clasen committed
98
99
100
101
102
103
104
 * given version.
 *
 * Generally you would pass in the constants %PANGO_VERSION_MAJOR,
 * %PANGO_VERSION_MINOR, %PANGO_VERSION_MICRO as the three arguments
 * to this function; that produces a check that the library in use at
 * run-time is compatible with the version of Pango the application or
 * module was compiled against.
105
106
107
108
109
110
111
112
113
114
 *
 * Compatibility is defined by two things: first the version
 * of the running library is newer than the version
 * @required_major.required_minor.@required_micro. Second
 * the running library must be binary compatible with the
 * version @required_major.required_minor.@required_micro
 * (same major version.)
 *
 * For compile-time version checking use PANGO_VERSION_CHECK().
 *
115
116
117
118
 * Return value: (nullable): %NULL if the Pango library is compatible
 *   with the given version, or a string describing the version
 *   mismatch.  The returned string is owned by Pango and should not
 *   be modified or freed.
119
120
 *
 * Since: 1.16
121
 */
122
123
const gchar*
pango_version_check (int required_major,
124
125
		     int required_minor,
		     int required_micro)
126
127
128
129
{
  gint pango_effective_micro = 100 * PANGO_VERSION_MINOR + PANGO_VERSION_MICRO;
  gint required_effective_micro = 100 * required_minor + required_micro;

130
131
  if (required_major > PANGO_VERSION_MAJOR)
    return "Pango version too old (major mismatch)";
132
133
134
135
136
137
138
139
140
  if (required_major < PANGO_VERSION_MAJOR)
    return "Pango version too new (major mismatch)";
  if (required_effective_micro < pango_effective_micro - PANGO_BINARY_AGE)
    return "Pango version too new (micro mismatch)";
  if (required_effective_micro > pango_effective_micro)
    return "Pango version too old (micro mismatch)";
  return NULL;
}

141
142
143
/**
 * pango_trim_string:
 * @str: a string
144
 *
Matthias Clasen's avatar
Matthias Clasen committed
145
 * Trims leading and trailing whitespace from a string.
146
 *
Matthias Clasen's avatar
Matthias Clasen committed
147
 * Return value: A newly-allocated string that must be freed with g_free()
Khaled Hosny's avatar
Khaled Hosny committed
148
 *
Matthias Clasen's avatar
Matthias Clasen committed
149
 * Deprecated: 1.38
150
 */
151
152
char *
pango_trim_string (const char *str)
Matthias Clasen's avatar
Matthias Clasen committed
153
154
155
156
157
158
{
  return _pango_trim_string (str);
}

char *
_pango_trim_string (const char *str)
159
160
161
162
{
  int len;

  g_return_val_if_fail (str != NULL, NULL);
163

164
  while (*str && g_ascii_isspace (*str))
165
166
167
    str++;

  len = strlen (str);
168
  while (len > 0 && g_ascii_isspace (str[len-1]))
169
170
171
172
173
174
175
    len--;

  return g_strndup (str, len);
}

/**
 * pango_split_file_list:
Behdad Esfahbod's avatar
Behdad Esfahbod committed
176
 * @str: a %G_SEARCHPATH_SEPARATOR separated list of filenames
177
 *
Behdad Esfahbod's avatar
Behdad Esfahbod committed
178
179
 * Splits a %G_SEARCHPATH_SEPARATOR-separated list of files, stripping
 * 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()
Khaled Hosny's avatar
Khaled Hosny committed
183
 *
Matthias Clasen's avatar
Matthias Clasen committed
184
 * Deprecated: 1.38
185
 */
186
187
188
189
190
191
192
char **
pango_split_file_list (const char *str)
{
  int i = 0;
  int j;
  char **files;

193
  files = g_strsplit (str, G_SEARCHPATH_SEPARATOR_S, -1);
194
195
196

  while (files[i])
    {
Matthias Clasen's avatar
Matthias Clasen committed
197
      char *file = _pango_trim_string (files[i]);
198
199
200
201
202
203

      /* If the resulting file is empty, skip it */
      if (file[0] == '\0')
	{
	  g_free(file);
	  g_free (files[i]);
204

205
206
	  for (j = i + 1; files[j]; j++)
	    files[j - 1] = files[j];
207

208
209
210
211
	  files[j - 1] = NULL;

	  continue;
	}
212
213
214
#ifndef G_OS_WIN32
      /* '~' is a quite normal and common character in file names on
       * Windows, especially in the 8.3 versions of long file names, which
Matthias Clasen's avatar
Matthias Clasen committed
215
       * still occur now and then. Also, few Windows user are aware of the
216
217
218
       * Unix shell convention that '~' stands for the home directory,
       * even if they happen to have a home directory.
       */
219
220
      if (file[0] == '~' && file[1] == G_DIR_SEPARATOR)
	{
221
	  char *tmp = g_strconcat (g_get_home_dir(), file + 1, NULL);
222
223
224
	  g_free (file);
	  file = tmp;
	}
225
226
227
228
229
      else if (file[0] == '~' && file[1] == '\0')
	{
	  g_free (file);
	  file = g_strdup (g_get_home_dir());
	}
230
#endif
231
232
      g_free (files[i]);
      files[i] = file;
233

234
235
236
237
238
239
240
241
242
      i++;
    }

  return files;
}

/**
 * pango_read_line:
 * @stream: a stdio stream
243
 * @str: `GString` buffer into which to write the result
244
 *
Matthias Clasen's avatar
Matthias Clasen committed
245
246
247
 * Reads an entire line from a file into a buffer.
 *
 * Lines may be delimited with '\n', '\r', '\n\r', or '\r\n'. The delimiter
248
249
 * is not written into the buffer. Text after a '#' character is treated as
 * a comment and skipped. '\' can be used to escape a # character.
Matthias Clasen's avatar
Matthias Clasen committed
250
 * '\' proceeding a line delimiter combines adjacent lines. A '\' proceeding
251
252
 * any other character is ignored and written into the output buffer
 * unmodified.
253
 *
Matthias Clasen's avatar
Matthias Clasen committed
254
255
256
 * Return value: 0 if the stream was already at an %EOF character,
 *   otherwise the number of lines read (this is useful for maintaining
 *   a line number counter which doesn't combine lines with '\')
Khaled Hosny's avatar
Khaled Hosny committed
257
 *
Matthias Clasen's avatar
Matthias Clasen committed
258
 * Deprecated: 1.38
259
 */
260
gint
261
262
263
264
265
pango_read_line (FILE *stream, GString *str)
{
  gboolean quoted = FALSE;
  gboolean comment = FALSE;
  int n_read = 0;
266
  int lines = 1;
267

268
269
270
  flockfile (stream);

  g_string_truncate (str, 0);
271

272
273
274
  while (1)
    {
      int c;
275

276
277
278
279
280
281
      c = getc_unlocked (stream);

      if (c == EOF)
	{
	  if (quoted)
	    g_string_append_c (str, '\\');
282

283
284
285
286
287
288
289
290
	  goto done;
	}
      else
	n_read++;

      if (quoted)
	{
	  quoted = FALSE;
291

292
293
294
295
296
297
298
299
300
301
	  switch (c)
	    {
	    case '#':
	      g_string_append_c (str, '#');
	      break;
	    case '\r':
	    case '\n':
	      {
		int next_c = getc_unlocked (stream);

302
		if (!(next_c == EOF ||
303
304
305
		      (c == '\r' && next_c == '\n') ||
		      (c == '\n' && next_c == '\r')))
		  ungetc (next_c, stream);
306
307

		lines++;
308

309
310
311
		break;
	      }
	    default:
312
	      g_string_append_c (str, '\\');
313
314
315
316
317
318
319
320
321
322
323
324
325
326
	      g_string_append_c (str, c);
	    }
	}
      else
	{
	  switch (c)
	    {
	    case '#':
	      comment = TRUE;
	      break;
	    case '\\':
	      if (!comment)
		quoted = TRUE;
	      break;
327
	    case '\r':
328
329
330
331
	    case '\n':
	      {
		int next_c = getc_unlocked (stream);

332
		if (!(next_c == EOF ||
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
		      (c == '\r' && next_c == '\n') ||
		      (c == '\n' && next_c == '\r')))
		  ungetc (next_c, stream);

		goto done;
	      }
	    default:
	      if (!comment)
		g_string_append_c (str, c);
	    }
	}
    }

 done:

  funlockfile (stream);

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.
Khaled Hosny's avatar
Khaled Hosny committed
361
 *
Matthias Clasen's avatar
Matthias Clasen committed
362
 * Deprecated: 1.38
363
 */
364
365
366
367
gboolean
pango_skip_space (const char **pos)
{
  const char *p = *pos;
368

369
  while (g_ascii_isspace (*p))
370
371
372
373
374
375
376
377
378
    p++;

  *pos = p;

  return !(*p == '\0');
}

/**
 * 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.
Matthias Clasen's avatar
Matthias Clasen committed
383
384
385
 *
 * A word consists of [A-Za-z_] followed by zero or more
 * [A-Za-z_0-9]. Leading white space is skipped.
386
 *
387
 * Return value: %FALSE if a parse error occurred
Khaled Hosny's avatar
Khaled Hosny committed
388
 *
Matthias Clasen's avatar
Matthias Clasen committed
389
 * Deprecated: 1.38
390
 */
391
392
393
394
395
gboolean
pango_scan_word (const char **pos, GString *out)
{
  const char *p = *pos;

396
  while (g_ascii_isspace (*p))
397
    p++;
398

399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
  if (!((*p >= 'A' && *p <= 'Z') ||
	(*p >= 'a' && *p <= 'z') ||
	*p == '_'))
    return FALSE;

  g_string_truncate (out, 0);
  g_string_append_c (out, *p);
  p++;

  while ((*p >= 'A' && *p <= 'Z') ||
	 (*p >= 'a' && *p <= 'z') ||
	 (*p >= '0' && *p <= '9') ||
	 *p == '_')
    {
      g_string_append_c (out, *p);
      p++;
    }

  *pos = p;

  return TRUE;
}

/**
 * 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.
Matthias Clasen's avatar
Matthias Clasen committed
428
429
430
 *
 * The string may either be a sequence of non-white-space characters,
 * 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
Khaled Hosny's avatar
Khaled Hosny committed
434
 *
Matthias Clasen's avatar
Matthias Clasen committed
435
 * Deprecated: 1.38
436
 */
437
438
439
440
gboolean
pango_scan_string (const char **pos, GString *out)
{
  const char *p = *pos;
441

442
  while (g_ascii_isspace (*p))
443
444
    p++;

445
  if (G_UNLIKELY (!*p))
446
447
448
449
450
451
452
453
454
455
456
457
458
    return FALSE;
  else if (*p == '"')
    {
      gboolean quoted = FALSE;
      g_string_truncate (out, 0);

      p++;

      while (TRUE)
	{
	  if (quoted)
	    {
	      int c = *p;
459

460
461
462
463
464
465
466
467
468
469
	      switch (c)
		{
		case '\0':
		  return FALSE;
		case 'n':
		  c = '\n';
		  break;
		case 't':
		  c = '\t';
		  break;
470
471
		default:
		  break;
472
		}
473

474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
	      quoted = FALSE;
	      g_string_append_c (out, c);
	    }
	  else
	    {
	      switch (*p)
		{
		case '\0':
		  return FALSE;
		case '\\':
		  quoted = TRUE;
		  break;
		case '"':
		  p++;
		  goto done;
		default:
		  g_string_append_c (out, *p);
		  break;
		}
	    }
	  p++;
	}
    done:
497
      ;
498
499
500
501
502
    }
  else
    {
      g_string_truncate (out, 0);

503
      while (*p && !g_ascii_isspace (*p))
504
505
506
507
508
509
510
511
512
513
514
	{
	  g_string_append_c (out, *p);
	  p++;
	}
    }

  *pos = p;

  return TRUE;
}

Matthias Clasen's avatar
Matthias Clasen committed
515
516
/**
 * pango_scan_int:
517
518
 * @pos: (inout): in/out string position
 * @out: (out): an int into which to write the result
519
520
 *
 * Scans an integer.
Matthias Clasen's avatar
Matthias Clasen committed
521
 *
Matthias Clasen's avatar
Matthias Clasen committed
522
 * Leading white space is skipped.
523
 *
524
 * Return value: %FALSE if a parse error occurred
Khaled Hosny's avatar
Khaled Hosny committed
525
 *
Matthias Clasen's avatar
Matthias Clasen committed
526
 * Deprecated: 1.38
527
 */
528
529
gboolean
pango_scan_int (const char **pos, int *out)
Matthias Clasen's avatar
Matthias Clasen committed
530
531
532
533
534
535
{
  return _pango_scan_int (pos, out);
}

gboolean
_pango_scan_int (const char **pos, int *out)
536
{
537
538
  char *end;
  long temp;
539

540
541
542
  errno = 0;
  temp = strtol (*pos, &end, 10);
  if (errno == ERANGE)
543
    {
544
545
546
      errno = 0;
      return FALSE;
    }
547

548
549
550
551
  *out = (int)temp;
  if ((long)(*out) != temp)
    {
      return FALSE;
552
553
    }

554
  *pos = end;
555
556
557
558

  return TRUE;
}

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
Khaled Hosny's avatar
Khaled Hosny committed
566
 *
Matthias Clasen's avatar
Matthias Clasen committed
567
 * Deprecated: 1.38
568
 */
569
570
571
char *
pango_config_key_get_system (const char *key)
{
Khaled Hosny's avatar
Khaled Hosny committed
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
Khaled Hosny's avatar
Khaled Hosny committed
582
 *
Matthias Clasen's avatar
Matthias Clasen committed
583
 * Deprecated: 1.38
584
 */
585
586
587
char *
pango_config_key_get (const char *key)
{
Khaled Hosny's avatar
Khaled Hosny committed
588
  return NULL;
589
}
590

Matthias Clasen's avatar
Matthias Clasen committed
591
592
593
/**
 * pango_get_sysconf_subdirectory:
 *
594
595
 * Returns the name of the "pango" subdirectory of SYSCONFDIR
 * (which is set at compile time).
Matthias Clasen's avatar
Matthias Clasen committed
596
597
 *
 * Return value: the Pango sysconf directory. The returned string should
598
 * not be freed.
599
 *
Matthias Clasen's avatar
Matthias Clasen committed
600
 * Deprecated: 1.38
Matthias Clasen's avatar
Matthias Clasen committed
601
 */
602
const char *
603
604
pango_get_sysconf_subdirectory (void)
{
605
  static const gchar *result = NULL; /* MT-safe */
606

607
  if (g_once_init_enter (&result))
608
    {
Behdad Esfahbod's avatar
Behdad Esfahbod committed
609
      const char *tmp_result = NULL;
610
611
      const char *sysconfdir = g_getenv ("PANGO_SYSCONFDIR");
      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
}

Matthias Clasen's avatar
Matthias Clasen committed
620
621
622
/**
 * pango_get_lib_subdirectory:
 *
623
624
 * Returns the name of the "pango" subdirectory of LIBDIR
 * (which is set at compile time).
Matthias Clasen's avatar
Matthias Clasen committed
625
626
 *
 * Return value: the Pango lib directory. The returned string should
627
 * not be freed.
628
 *
Matthias Clasen's avatar
Matthias Clasen committed
629
 * Deprecated: 1.38
Matthias Clasen's avatar
Matthias Clasen committed
630
 */
631
const char *
632
633
pango_get_lib_subdirectory (void)
{
634
  static const gchar *result = NULL; /* MT-safe */
635

636
  if (g_once_init_enter (&result))
637
    {
Behdad Esfahbod's avatar
Behdad Esfahbod committed
638
      const gchar *tmp_result = NULL;
639
640
      const char *libdir = g_getenv ("PANGO_LIBDIR");
      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
651
652
653
654
static gboolean
parse_int (const char *word,
	   int        *out)
{
  char *end;
655
656
657
658
659
660
661
662
  long val;
  int i;

  if (word == NULL)
    return FALSE;

  val = strtol (word, &end, 10);
  i = val;
663
664
665
666
667
668
669
670
671
672
673
674

  if (end != word && *end == '\0' && val >= 0 && val == i)
    {
      if (out)
        *out = i;

      return TRUE;
    }

  return FALSE;
}

675
676
/**
 * pango_parse_enum:
677
678
679
680
681
682
 * @type: enum type to parse, eg. %PANGO_TYPE_ELLIPSIZE_MODE
 * @str: (nullable): string to parse
 * @value: (out) (optional): integer to store the result in
 * @warn: if %TRUE, issue a g_warning() on bad input
 * @possible_values: (out) (optional): place to store list of possible
 *   values on failure
683
 *
684
 * Parses an enum type and stores the result in @value.
685
 *
686
687
688
689
690
 * If @str does not match the nick name of any of the possible values
 * for the enum and is not an integer, %FALSE is returned, a warning
 * is issued if @warn is %TRUE, and a string representing the list of
 * possible values is stored in @possible_values. The list is
 * slash-separated, eg. "none/start/middle/end".
691
 *
692
693
694
695
 * If failed and @possible_values is not %NULL, returned string should
 * be freed using g_free().
 *
 * Return value: %TRUE if @str was successfully parsed
696
 *
Matthias Clasen's avatar
Matthias Clasen committed
697
 * Deprecated: 1.38
Khaled Hosny's avatar
Khaled Hosny committed
698
 *
699
 * Since: 1.16
700
 */
701
702
703
704
705
706
gboolean
pango_parse_enum (GType       type,
		  const char *str,
		  int        *value,
		  gboolean    warn,
		  char      **possible_values)
Matthias Clasen's avatar
Matthias Clasen committed
707
708
709
710
711
712
713
714
715
716
{
  return _pango_parse_enum (type, str, value, warn, possible_values);
}

gboolean
_pango_parse_enum (GType       type,
		   const char *str,
 		   int        *value,
		   gboolean    warn,
		   char      **possible_values)
717
718
719
720
721
722
723
724
725
726
727
728
729
{
  GEnumClass *class = NULL;
  gboolean ret = TRUE;
  GEnumValue *v = NULL;

  class = g_type_class_ref (type);

  if (G_LIKELY (str))
    v = g_enum_get_value_by_nick (class, str);

  if (v)
    {
      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
738
739
740
741
742
743
	  int i;
	  GString *s = g_string_new (NULL);

	  for (i = 0, v = g_enum_get_value (class, i); v;
	       i++  , v = g_enum_get_value (class, i))
	    {
	      if (i)
744
		g_string_append_c (s, '/');
745
746
747
748
749
750
751
752
753
754
	      g_string_append (s, v->value_nick);
	    }

	  if (warn)
	    g_warning ("%s must be one of %s",
		       G_ENUM_CLASS_TYPE_NAME(class),
		       s->str);

	  if (possible_values)
	    *possible_values = s->str;
755

756
757
758
759
760
761
762
763
764
	  g_string_free (s, possible_values ? FALSE : TRUE);
	}
    }

  g_type_class_unref (class);

  return ret;
}

765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
gboolean
pango_parse_flags (GType        type,
                   const char  *str,
                   int         *value,
                   char       **possible_values)
{
  GFlagsClass *class = NULL;
  gboolean ret = TRUE;
  GFlagsValue *v = NULL;

  class = g_type_class_ref (type);

  v = g_flags_get_value_by_nick (class, str);

  if (v)
    {
      *value = v->value;
    }
  else if (!parse_int (str, value))
    {
      char **strv = g_strsplit (str, "|", 0);
      int i;

      *value = 0;

      for (i = 0; strv[i]; i++)
        {
          strv[i] = g_strstrip (strv[i]);
          v = g_flags_get_value_by_nick (class, strv[i]);
          if (!v)
            {
              ret = FALSE;
              break;
            }
          *value |= v->value;
        }
      g_strfreev (strv);

      if (!ret && possible_values)
	{
	  int i;
	  GString *s = g_string_new (NULL);

          for (i = 0; i < class->n_values; i++)
            {
              v = &class->values[i];
              if (i)
                g_string_append_c (s, '/');
              g_string_append (s, v->value_nick);
            }

          *possible_values = s->str;

          g_string_free (s, FALSE);
	}
    }

  g_type_class_unref (class);

  return ret;
}

827
828
/**
 * pango_lookup_aliases:
829
830
831
832
 * @fontname: an ASCII string
 * @families: (out) (array length=n_families): will be set to an array of
 *   font family names. This array is owned by Pango and should not be freed
 * @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.
Matthias Clasen's avatar
Matthias Clasen committed
835
 *
836
837
 * The resulting font family names will be stored in @families,
 * and the number of families in @n_families.
838
839
840
 *
 * Deprecated: 1.32: This function is not thread-safe.
 */
841
842
843
844
845
void
pango_lookup_aliases (const char   *fontname,
		      char       ***families,
		      int          *n_families)
{
846
847
  *families = NULL;
  *n_families = 0;
848
}
849

850
851
852
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"

853
854
/**
 * pango_find_base_dir:
855
 * @text: the text to process. Must be valid UTF-8
856
857
 * @length: length of @text in bytes (may be -1 if @text is nul-terminated)
 *
858
 * Searches a string the first character that has a strong
859
860
861
 * direction, according to the Unicode bidirectional algorithm.
 *
 * 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
867
868
869
870
871
872
 */
PangoDirection
pango_find_base_dir (const gchar *text,
		     gint         length)
{
  PangoDirection dir = PANGO_DIRECTION_NEUTRAL;
  const gchar *p;

873
  g_return_val_if_fail (text != NULL || length == 0, PANGO_DIRECTION_NEUTRAL);
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889

  p = text;
  while ((length < 0 || p < text + length) && *p)
    {
      gunichar wc = g_utf8_get_char (p);

      dir = pango_unichar_direction (wc);

      if (dir != PANGO_DIRECTION_NEUTRAL)
	break;

      p = g_utf8_next_char (p);
    }

  return dir;
}
Behdad Esfahbod's avatar
Behdad Esfahbod committed
890

891
892
#pragma GCC diagnostic pop

Behdad Esfahbod's avatar
Behdad Esfahbod committed
893
894
/**
 * pango_is_zero_width:
895
 * @ch: a Unicode character
Behdad Esfahbod's avatar
Behdad Esfahbod committed
896
 *
Matthias Clasen's avatar
Matthias Clasen committed
897
898
899
900
901
 * Checks if a character that should not be normally rendered.
 *
 * This includes all Unicode characters with "ZERO WIDTH" in their name,
 * as well as *bidi* formatting characters, and a few other ones.  This is
 * totally different from g_unichar_iszerowidth() and is at best misnamed.
Behdad Esfahbod's avatar
Behdad Esfahbod committed
902
903
 *
 * Return value: %TRUE if @ch is a zero-width character, %FALSE otherwise
904
905
 *
 * Since: 1.10
Behdad Esfahbod's avatar
Behdad Esfahbod committed
906
907
908
909
910
 */
gboolean
pango_is_zero_width (gunichar ch)
{
/* Zero Width characters:
911
912
913
 *
 *  00AD  SOFT HYPHEN
 *  034F  COMBINING GRAPHEME JOINER
Behdad Esfahbod's avatar
Behdad Esfahbod committed
914
915
916
917
918
919
 *
 *  200B  ZERO WIDTH SPACE
 *  200C  ZERO WIDTH NON-JOINER
 *  200D  ZERO WIDTH JOINER
 *  200E  LEFT-TO-RIGHT MARK
 *  200F  RIGHT-TO-LEFT MARK
920
 *
Behdad Esfahbod's avatar
Behdad Esfahbod committed
921
 *  2028  LINE SEPARATOR
922
 *
923
924
925
926
927
928
929
930
931
932
 *  2060  WORD JOINER
 *  2061  FUNCTION APPLICATION
 *  2062  INVISIBLE TIMES
 *  2063  INVISIBLE SEPARATOR
 *
 *  2066  LEFT-TO-RIGHT ISOLATE
 *  2067  RIGHT-TO-LEFT ISOLATE
 *  2068  FIRST STRONG ISOLATE
 *  2069  POP DIRECTIONAL ISOLATE
 *
Behdad Esfahbod's avatar
Behdad Esfahbod committed
933
934
935
936
937
 *  202A  LEFT-TO-RIGHT EMBEDDING
 *  202B  RIGHT-TO-LEFT EMBEDDING
 *  202C  POP DIRECTIONAL FORMATTING
 *  202D  LEFT-TO-RIGHT OVERRIDE
 *  202E  RIGHT-TO-LEFT OVERRIDE
938
 *
Behdad Esfahbod's avatar
Behdad Esfahbod committed
939
940
 *  FEFF  ZERO WIDTH NO-BREAK SPACE
 */
941
  return ((ch & ~(gunichar)0x007F) == 0x2000 && (
Behdad Esfahbod's avatar
Behdad Esfahbod committed
942
		(ch >= 0x200B && ch <= 0x200F) ||
943
944
		(ch >= 0x202A && ch <= 0x202E) ||
		(ch >= 0x2060 && ch <= 0x2063) ||
945
                (ch >= 0x2066 && ch <= 0x2069) ||
946
947
948
949
		(ch == 0x2028)
	 )) || G_UNLIKELY (ch == 0x00AD
			|| ch == 0x034F
			|| ch == 0xFEFF);
Behdad Esfahbod's avatar
Behdad Esfahbod committed
950
}
951
952
953

/**
 * pango_quantize_line_geometry:
954
955
 * @thickness: (inout): pointer to the thickness of a line, in Pango units
 * @position: (inout): corresponding position
956
 *
Matthias Clasen's avatar
Matthias Clasen committed
957
958
959
960
 * Quantizes the thickness and position of a line to whole device pixels.
 *
 * This is typically used for underline or strikethrough. The purpose of
 * this function is to avoid such lines looking blurry.
961
 *
962
963
964
 * Care is taken to make sure @thickness is at least one pixel when this
 * function returns, but returned @position may become zero as a result
 * of rounding.
965
 *
966
967
968
969
970
971
972
973
974
 * Since: 1.12
 */
void
pango_quantize_line_geometry (int *thickness,
			      int *position)
{
  int thickness_pixels = (*thickness + PANGO_SCALE / 2) / PANGO_SCALE;
  if (thickness_pixels == 0)
    thickness_pixels = 1;
975

976
977
978
979
980
981
982
983
984
985
986
987
988
  if (thickness_pixels & 1)
    {
      int new_center = ((*position - *thickness / 2) & ~(PANGO_SCALE - 1)) + PANGO_SCALE / 2;
      *position = new_center + (PANGO_SCALE * thickness_pixels) / 2;
    }
  else
    {
      int new_center = ((*position - *thickness / 2 + PANGO_SCALE / 2) & ~(PANGO_SCALE - 1));
      *position = new_center + (PANGO_SCALE * thickness_pixels) / 2;
    }

  *thickness = thickness_pixels * PANGO_SCALE;
}
989

990
991
992
993
/**
 * pango_units_from_double:
 * @d: double floating-point value
 *
Matthias Clasen's avatar
Matthias Clasen committed
994
995
996
997
 * Converts a floating-point number to Pango units.
 *
 * The conversion is done by multiplying @d by %PANGO_SCALE and
 * rounding the result to nearest integer.
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
 *
 * Return value: the value in Pango units.
 *
 * Since: 1.16
 */
int
pango_units_from_double (double d)
{
  return (int)floor (d * PANGO_SCALE + 0.5);
}

/**
 * pango_units_to_double:
 * @i: value in Pango units
 *
Matthias Clasen's avatar
Matthias Clasen committed
1013
1014
1015
 * Converts a number in Pango units to floating-point.
 *
 * The conversion is done by dividing @i by %PANGO_SCALE.
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
 *
 * Return value: the double value.
 *
 * Since: 1.16
 */
double
pango_units_to_double (int i)
{
  return (double)i / PANGO_SCALE;
}

/**
 * pango_extents_to_pixels:
1029
1030
 * @inclusive: (nullable): rectangle to round to pixels inclusively
 * @nearest: (nullable): rectangle to round to nearest pixels
1031
 *
Matthias Clasen's avatar
Matthias Clasen committed
1032
1033
1034
1035
 * Converts extents from Pango units to device units.
 *
 * The conversion is done by dividing by the %PANGO_SCALE factor and
 * performing rounding.
1036
 *
Matthias Clasen's avatar
Matthias Clasen committed
1037
1038
1039
 * The @inclusive rectangle is converted by flooring the x/y coordinates
 * and extending width/height, such that the final rectangle completely
 * includes the original rectangle.
1040
 *
1041
1042
 * The @nearest rectangle is converted by rounding the coordinates
 * 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
Matthias Clasen's avatar
Matthias Clasen committed
1045
1046
 * rectangle to completely contain the original rectangle, pass it in as
 * @inclusive. If you want two touching-but-not-overlapping rectangles stay
1047
1048
 * touching-but-not-overlapping after rounding to device units, pass them in
 * as @nearest.
1049
1050
 *
 * Since: 1.16
1051
 */
1052
void
1053
1054
pango_extents_to_pixels (PangoRectangle *inclusive,
			 PangoRectangle *nearest)
1055
{
1056
  if (inclusive)
1057
    {
1058
1059
      int orig_x = inclusive->x;
      int orig_y = inclusive->y;
1060

1061
1062
      inclusive->x = PANGO_PIXELS_FLOOR (inclusive->x);
      inclusive->y = PANGO_PIXELS_FLOOR (inclusive->y);
1063

1064
1065
      inclusive->width  = PANGO_PIXELS_CEIL (orig_x + inclusive->width ) - inclusive->x;
      inclusive->height = PANGO_PIXELS_CEIL (orig_y + inclusive->height) - inclusive->y;
1066
1067
    }

1068
  if (nearest)
1069
    {
1070
1071
      int orig_x = nearest->x;
      int orig_y = nearest->y;
1072

1073
1074
      nearest->x = PANGO_PIXELS (nearest->x);
      nearest->y = PANGO_PIXELS (nearest->y);
1075

1076
1077
      nearest->width  = PANGO_PIXELS (orig_x + nearest->width ) - nearest->x;
      nearest->height = PANGO_PIXELS (orig_y + nearest->height) - nearest->y;
1078
1079
    }
}
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090





/*********************************************************
 * Some internal functions for handling PANGO_ATTR_SHAPE *
 ********************************************************/

void
_pango_shape_shape (const char       *text,
Behdad Esfahbod's avatar
Behdad Esfahbod committed
1091
		    unsigned int      n_chars,
Behdad Esfahbod's avatar
Behdad Esfahbod committed
1092
		    PangoRectangle   *shape_ink G_GNUC_UNUSED,
1093
1094
1095
		    PangoRectangle   *shape_logical,
		    PangoGlyphString *glyphs)
{
Behdad Esfahbod's avatar
Behdad Esfahbod committed
1096
  unsigned int i;
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
  const char *p;

  pango_glyph_string_set_size (glyphs, n_chars);

  for (i=0, p = text; i < n_chars; i++, p = g_utf8_next_char (p))
    {
      glyphs->glyphs[i].glyph = PANGO_GLYPH_EMPTY;
      glyphs->glyphs[i].geometry.x_offset = 0;
      glyphs->glyphs[i].geometry.y_offset = 0;
      glyphs->glyphs[i].geometry.width = shape_logical->width;
      glyphs->glyphs[i].attr.is_cluster_start = 1;

      glyphs->log_clusters[i] = p - text;
    }
}

void
_pango_shape_get_extents (gint              n_chars,
			  PangoRectangle   *shape_ink,
			  PangoRectangle   *shape_logical,
			  PangoRectangle   *ink_rect,
			  PangoRectangle   *logical_rect)
{
  if (n_chars > 0)
    {
      if (ink_rect)
	{
	  ink_rect->x = MIN (shape_ink->x, shape_ink->x + shape_logical->width * (n_chars - 1));
	  ink_rect->width = MAX (shape_ink->width, shape_ink->width + shape_logical->width * (n_chars - 1));
	  ink_rect->y = shape_ink->y;
	  ink_rect->height = shape_ink->height;
	}
      if (logical_rect)
	{
	  logical_rect->x = MIN (shape_logical->x, shape_logical->x + shape_logical->width * (n_chars - 1));
	  logical_rect->width = MAX (shape_logical->width, shape_logical->width + shape_logical->width * (n_chars - 1));
	  logical_rect->y = shape_logical->y;
	  logical_rect->height = shape_logical->height;
	}
    }
  else
    {
      if (ink_rect)
	{
	  ink_rect->x = 0;
	  ink_rect->y = 0;
	  ink_rect->width = 0;
	  ink_rect->height = 0;
	}

      if (logical_rect)
	{
	  logical_rect->x = 0;
	  logical_rect->y = 0;
	  logical_rect->width = 0;
	  logical_rect->height = 0;
	}
    }
}

1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
/**
 * pango_find_paragraph_boundary:
 * @text: UTF-8 text
 * @length: length of @text in bytes, or -1 if nul-terminated
 * @paragraph_delimiter_index: (out): return location for index of
 *   delimiter
 * @next_paragraph_start: (out): return location for start of next
 *   paragraph
 *
 * Locates a paragraph boundary in @text.
 *
 * A boundary is caused by delimiter characters, such as
 * a newline, carriage return, carriage return-newline pair,
 * or Unicode paragraph separator character.
 *
 * The index of the run of delimiters is returned in
 * @paragraph_delimiter_index. The index of the start
 * of the paragrap (index after all delimiters) is stored
 * in @next_paragraph_start.
 *
 * If no delimiters are found, both @paragraph_delimiter_index
 * and @next_paragraph_start are filled with the length of @text
 * (an index one off the end).
 */
void
pango_find_paragraph_boundary (const char *text,
                               int         length,
                               int        *paragraph_delimiter_index,
                               int        *next_paragraph_start)
{
  const char *p = text;
  const char *end;
  const char *start = NULL;
  const char *delimiter = NULL;

  /* Only one character has type G_UNICODE_PARAGRAPH_SEPARATOR in
   * Unicode 5.0; update the following code if that changes.
   */

  /* prev_sep is the first byte of the previous separator.  Since
   * the valid separators are \r, \n, and PARAGRAPH_SEPARATOR, the
   * first byte is enough to identify it.
   */
  char prev_sep;

#define PARAGRAPH_SEPARATOR_STRING "\xE2\x80\xA9"

  if (length < 0)
    length = strlen (text);

  end = text + length;

  if (paragraph_delimiter_index)
    *paragraph_delimiter_index = length;

  if (next_paragraph_start)
    *next_paragraph_start = length;

  if (length == 0)
    return;

  prev_sep = 0;
  while (p < end)
    {
      if (prev_sep == '\n' ||
          prev_sep == PARAGRAPH_SEPARATOR_STRING[0])
        {
          g_assert (delimiter);
          start = p;
          break;
        }
      else if (prev_sep == '\r')
        {
          /* don't break between \r and \n */
          if (*p != '\n')
            {
              g_assert (delimiter);
              start = p;
              break;
            }
        }

      if (*p == '\n' ||
           *p == '\r' ||
           !strncmp(p, PARAGRAPH_SEPARATOR_STRING, strlen (PARAGRAPH_SEPARATOR_STRING)))
        {
          if (delimiter == NULL)
            delimiter = p;
          prev_sep = *p;
        }
      else
        prev_sep = 0;

      p = g_utf8_next_char (p);
    }

  if (delimiter && paragraph_delimiter_index)
    *paragraph_delimiter_index = delimiter - text;

  if (start && next_paragraph_start)
    *next_paragraph_start = start - text;
}