1d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod/*
22409d5f8d7dd8b535ce5ea29e933f7db27d33793Behdad Esfahbod * Copyright © 2009  Red Hat, Inc.
32409d5f8d7dd8b535ce5ea29e933f7db27d33793Behdad Esfahbod * Copyright © 2009  Keith Stribley
42409d5f8d7dd8b535ce5ea29e933f7db27d33793Behdad Esfahbod * Copyright © 2011  Google, Inc.
5d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod *
6c755cb3e3ac55156d0d2ec05adea7a650b97cc41Behdad Esfahbod *  This is part of HarfBuzz, a text shaping library.
7d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod *
8d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod * Permission is hereby granted, without written agreement and without
9d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod * license or royalty fees, to use, copy, modify, and distribute this
10d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod * software and its documentation for any purpose, provided that the
11d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod * above copyright notice and the following two paragraphs appear in
12d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod * all copies of this software.
13d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod *
14d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
15d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
16d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
17d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
18d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod * DAMAGE.
19d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod *
20d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
21d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
22d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
23d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
24d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
25d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod *
26d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod * Red Hat Author(s): Behdad Esfahbod
272409d5f8d7dd8b535ce5ea29e933f7db27d33793Behdad Esfahbod * Google Author(s): Behdad Esfahbod
28d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod */
29d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod
30c57d454accff66e5f2c58006e8fb40bc020b6182Behdad Esfahbod#include "hb-private.hh"
31d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod
32d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod#include "hb-icu.h"
33d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod
34fb194b8794898f51eb596fa4092c26606889d376Behdad Esfahbod#include "hb-unicode-private.hh"
35d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod
36d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod#include <unicode/uchar.h>
37498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod#include <unicode/unorm.h>
3836a4f4a482456ee816dcb59befa0b0538ba487dfBehdad Esfahbod#include <unicode/ustring.h>
394ac4c6f2e12ddc8bf5e750671321458218b6e0c8Behdad Esfahbod#include <unicode/uversion.h>
40acdba3f90b232fc12fcb200dca2584481b339118Behdad Esfahbod
41acdba3f90b232fc12fcb200dca2584481b339118Behdad Esfahbod
42f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbodhb_script_t
43f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbodhb_icu_script_to_script (UScriptCode script)
44d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod{
454d559cddbb3b3a5c12c5167eba69598618a9f283Behdad Esfahbod  if (unlikely (script == USCRIPT_INVALID_CODE))
464d559cddbb3b3a5c12c5167eba69598618a9f283Behdad Esfahbod    return HB_SCRIPT_INVALID;
474d559cddbb3b3a5c12c5167eba69598618a9f283Behdad Esfahbod
484c9fe88d30036340fe592bcbc375049b84602b8bBehdad Esfahbod  return hb_script_from_string (uscript_getShortName (script), -1);
49d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod}
50d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod
51f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad EsfahbodUScriptCode
52f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbodhb_icu_script_from_script (hb_script_t script)
53d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod{
544d559cddbb3b3a5c12c5167eba69598618a9f283Behdad Esfahbod  if (unlikely (script == HB_SCRIPT_INVALID))
554d559cddbb3b3a5c12c5167eba69598618a9f283Behdad Esfahbod    return USCRIPT_INVALID_CODE;
56afab01cf7caca79cf6dfabe6827d1703be1a74f7Behdad Esfahbod
574d559cddbb3b3a5c12c5167eba69598618a9f283Behdad Esfahbod  for (unsigned int i = 0; i < USCRIPT_CODE_LIMIT; i++)
584d559cddbb3b3a5c12c5167eba69598618a9f283Behdad Esfahbod    if (unlikely (hb_icu_script_to_script ((UScriptCode) i) == script))
594d559cddbb3b3a5c12c5167eba69598618a9f283Behdad Esfahbod      return (UScriptCode) i;
602fd0c577e322ccbf762927bc4600b3ea31db4c80Ryan Lortie
61f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  return USCRIPT_UNKNOWN;
62f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod}
63f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod
64f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod
6521fdcee00125b6e1c09f0bed3064d16ccd3a7a5dBehdad Esfahbodstatic hb_unicode_combining_class_t
66fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbodhb_icu_unicode_combining_class (hb_unicode_funcs_t *ufuncs HB_UNUSED,
67fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbod				hb_codepoint_t      unicode,
68fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbod				void               *user_data HB_UNUSED)
69f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod
70f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod{
7121fdcee00125b6e1c09f0bed3064d16ccd3a7a5dBehdad Esfahbod  return (hb_unicode_combining_class_t) u_getCombiningClass (unicode);
72f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod}
73f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod
74f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbodstatic unsigned int
75fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbodhb_icu_unicode_eastasian_width (hb_unicode_funcs_t *ufuncs HB_UNUSED,
76fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbod				hb_codepoint_t      unicode,
77fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbod				void               *user_data HB_UNUSED)
78f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod{
79f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  switch (u_getIntPropertyValue(unicode, UCHAR_EAST_ASIAN_WIDTH))
80f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  {
81f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_EA_WIDE:
82f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_EA_FULLWIDTH:
83f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod    return 2;
84f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_EA_NEUTRAL:
85f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_EA_AMBIGUOUS:
86f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_EA_HALFWIDTH:
87f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_EA_NARROW:
88f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod    return 1;
89f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  }
90f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  return 1;
91f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod}
92f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod
93f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbodstatic hb_unicode_general_category_t
94fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbodhb_icu_unicode_general_category (hb_unicode_funcs_t *ufuncs HB_UNUSED,
95fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbod				 hb_codepoint_t      unicode,
96fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbod				 void               *user_data HB_UNUSED)
97f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod{
98f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  switch (u_getIntPropertyValue(unicode, UCHAR_GENERAL_CATEGORY))
99f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  {
100f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_UNASSIGNED:			return HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED;
101f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod
102f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_UPPERCASE_LETTER:		return HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER;
103f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_LOWERCASE_LETTER:		return HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER;
104f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_TITLECASE_LETTER:		return HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER;
105f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_MODIFIER_LETTER:		return HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER;
106f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_OTHER_LETTER:			return HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER;
107f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod
108f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_NON_SPACING_MARK:		return HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK;
109f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_ENCLOSING_MARK:		return HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK;
1105157e12a55f943b7fc5be7dce0b2ee1bcacca6ecBehdad Esfahbod  case U_COMBINING_SPACING_MARK:	return HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK;
111f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod
112f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_DECIMAL_DIGIT_NUMBER:		return HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER;
113f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_LETTER_NUMBER:			return HB_UNICODE_GENERAL_CATEGORY_LETTER_NUMBER;
114f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_OTHER_NUMBER:			return HB_UNICODE_GENERAL_CATEGORY_OTHER_NUMBER;
115f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod
116f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_SPACE_SEPARATOR:		return HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR;
117f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_LINE_SEPARATOR:		return HB_UNICODE_GENERAL_CATEGORY_LINE_SEPARATOR;
118f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_PARAGRAPH_SEPARATOR:		return HB_UNICODE_GENERAL_CATEGORY_PARAGRAPH_SEPARATOR;
119f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod
120f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_CONTROL_CHAR:			return HB_UNICODE_GENERAL_CATEGORY_CONTROL;
121f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_FORMAT_CHAR:			return HB_UNICODE_GENERAL_CATEGORY_FORMAT;
122f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_PRIVATE_USE_CHAR:		return HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE;
123f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_SURROGATE:			return HB_UNICODE_GENERAL_CATEGORY_SURROGATE;
124f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod
125f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod
126f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_DASH_PUNCTUATION:		return HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION;
127f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_START_PUNCTUATION:		return HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION;
128f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_END_PUNCTUATION:		return HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION;
129f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_CONNECTOR_PUNCTUATION:		return HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION;
130f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_OTHER_PUNCTUATION:		return HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION;
131f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod
132f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_MATH_SYMBOL:			return HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL;
133f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_CURRENCY_SYMBOL:		return HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL;
134f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_MODIFIER_SYMBOL:		return HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL;
135f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_OTHER_SYMBOL:			return HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL;
136f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod
137f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_INITIAL_PUNCTUATION:		return HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION;
138f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  case U_FINAL_PUNCTUATION:		return HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION;
139f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  }
140f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod
141f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  return HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED;
142f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod}
143f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod
144f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbodstatic hb_codepoint_t
145fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbodhb_icu_unicode_mirroring (hb_unicode_funcs_t *ufuncs HB_UNUSED,
146fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbod			  hb_codepoint_t      unicode,
147fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbod			  void               *user_data HB_UNUSED)
148f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod{
149f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  return u_charMirror(unicode);
150f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod}
151f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod
152f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbodstatic hb_script_t
153fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbodhb_icu_unicode_script (hb_unicode_funcs_t *ufuncs HB_UNUSED,
154fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbod		       hb_codepoint_t      unicode,
155fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbod		       void               *user_data HB_UNUSED)
156f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod{
157f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  UErrorCode status = U_ZERO_ERROR;
158f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  UScriptCode scriptCode = uscript_getScript(unicode, &status);
159f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod
160889caa52fa1bef61013ec1d127f84d7d5907ef1eBehdad Esfahbod  if (unlikely (U_FAILURE (status)))
16103034acb8a9fdd33135bc3775a1f932da9ebdd42Behdad Esfahbod    return HB_SCRIPT_UNKNOWN;
16203034acb8a9fdd33135bc3775a1f932da9ebdd42Behdad Esfahbod
163f144a8ea840c6452c1fece2fd988b42a8ea7c5a6Behdad Esfahbod  return hb_icu_script_to_script (scriptCode);
164d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod}
165d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod
166d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod#if U_ICU_VERSION_MAJOR_NUM >= 49
167d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbodstatic const UNormalizer2 *normalizer;
168d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod#endif
169d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod
170fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbodstatic hb_bool_t
171fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbodhb_icu_unicode_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
172fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbod			hb_codepoint_t      a,
173fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbod			hb_codepoint_t      b,
174fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbod			hb_codepoint_t     *ab,
175fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbod			void               *user_data HB_UNUSED)
176fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbod{
177d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod#if U_ICU_VERSION_MAJOR_NUM >= 49
178d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod  {
179d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod    UChar32 ret = unorm2_composePair (normalizer, a, b);
180d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod    if (ret < 0) return false;
181d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod    *ab = ret;
182d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod    return true;
183d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod  }
184d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod#endif
185d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod
186d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod  /* We don't ifdef-out the fallback code such that compiler always
187d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod   * sees it and makes sure it's compilable. */
188498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod
189498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod  UChar utf16[4], normalized[5];
190b1914b8bd08ecdea79930dda7e3bb2ae9e6134a1Behdad Esfahbod  unsigned int len;
191498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod  hb_bool_t ret, err;
192498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod  UErrorCode icu_err;
193498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod
194498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod  len = 0;
1950594a2448440208efa0acac9a5d8d52d43108289Behdad Esfahbod  err = false;
196498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod  U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), a, err);
1970594a2448440208efa0acac9a5d8d52d43108289Behdad Esfahbod  if (err) return false;
198498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod  U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), b, err);
1990594a2448440208efa0acac9a5d8d52d43108289Behdad Esfahbod  if (err) return false;
200498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod
201498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod  icu_err = U_ZERO_ERROR;
202498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod  len = unorm_normalize (utf16, len, UNORM_NFC, 0, normalized, ARRAY_LENGTH (normalized), &icu_err);
203889caa52fa1bef61013ec1d127f84d7d5907ef1eBehdad Esfahbod  if (U_FAILURE (icu_err))
2040594a2448440208efa0acac9a5d8d52d43108289Behdad Esfahbod    return false;
20536a4f4a482456ee816dcb59befa0b0538ba487dfBehdad Esfahbod  if (u_countChar32 (normalized, len) == 1) {
206498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod    U16_GET_UNSAFE (normalized, 0, *ab);
2070594a2448440208efa0acac9a5d8d52d43108289Behdad Esfahbod    ret = true;
208498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod  } else {
2090594a2448440208efa0acac9a5d8d52d43108289Behdad Esfahbod    ret = false;
210498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod  }
211498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod
212498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod  return ret;
213fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbod}
214fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbod
215fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbodstatic hb_bool_t
216fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbodhb_icu_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
217fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbod			  hb_codepoint_t      ab,
218fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbod			  hb_codepoint_t     *a,
219fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbod			  hb_codepoint_t     *b,
220fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbod			  void               *user_data HB_UNUSED)
221fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbod{
222d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod#if U_ICU_VERSION_MAJOR_NUM >= 49
223d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod  {
224d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod    UChar decomposed[4];
225d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod    int len;
226d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod    UErrorCode icu_err = U_ZERO_ERROR;
227d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod    len = unorm2_getRawDecomposition (normalizer, ab, decomposed,
228d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod				      ARRAY_LENGTH (decomposed), &icu_err);
229d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod    if (U_FAILURE (icu_err) || len < 0) return false;
230d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod
231d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod    len = u_countChar32 (decomposed, len);
232d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod    if (len == 1) {
233d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod      U16_GET_UNSAFE (decomposed, 0, *a);
234d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod      *b = 0;
235d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod      return *a != ab;
236d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod    } else if (len == 2) {
237d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod      len =0;
238d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod      U16_NEXT_UNSAFE (decomposed, len, *a);
239d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod      U16_NEXT_UNSAFE (decomposed, len, *b);
240d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod    }
241d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod    return true;
242d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod  }
243d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod#endif
244d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod
245d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod  /* We don't ifdef-out the fallback code such that compiler always
246d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod   * sees it and makes sure it's compilable. */
247d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod
248378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod  UChar utf16[2], normalized[2 * HB_UNICODE_MAX_DECOMPOSITION_LEN + 1];
249b1914b8bd08ecdea79930dda7e3bb2ae9e6134a1Behdad Esfahbod  unsigned int len;
250498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod  hb_bool_t ret, err;
251498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod  UErrorCode icu_err;
252498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod
25363c0ef4a0763e579c9c80887bbfbd2651de05067Behdad Esfahbod  /* This function is a monster! Maybe it wasn't a good idea adding a
25463c0ef4a0763e579c9c80887bbfbd2651de05067Behdad Esfahbod   * pairwise decompose API... */
25563c0ef4a0763e579c9c80887bbfbd2651de05067Behdad Esfahbod  /* Watchout for the dragons.  Err, watchout for macros changing len. */
25663c0ef4a0763e579c9c80887bbfbd2651de05067Behdad Esfahbod
257498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod  len = 0;
2580594a2448440208efa0acac9a5d8d52d43108289Behdad Esfahbod  err = false;
259498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod  U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), ab, err);
2600594a2448440208efa0acac9a5d8d52d43108289Behdad Esfahbod  if (err) return false;
261498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod
262498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod  icu_err = U_ZERO_ERROR;
263498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod  len = unorm_normalize (utf16, len, UNORM_NFD, 0, normalized, ARRAY_LENGTH (normalized), &icu_err);
264889caa52fa1bef61013ec1d127f84d7d5907ef1eBehdad Esfahbod  if (U_FAILURE (icu_err))
2650594a2448440208efa0acac9a5d8d52d43108289Behdad Esfahbod    return false;
266498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod
26736a4f4a482456ee816dcb59befa0b0538ba487dfBehdad Esfahbod  len = u_countChar32 (normalized, len);
268498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod
269498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod  if (len == 1) {
270498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod    U16_GET_UNSAFE (normalized, 0, *a);
271498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod    *b = 0;
272498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod    ret = *a != ab;
273498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod  } else if (len == 2) {
27463c0ef4a0763e579c9c80887bbfbd2651de05067Behdad Esfahbod    len =0;
27563c0ef4a0763e579c9c80887bbfbd2651de05067Behdad Esfahbod    U16_NEXT_UNSAFE (normalized, len, *a);
27663c0ef4a0763e579c9c80887bbfbd2651de05067Behdad Esfahbod    U16_NEXT_UNSAFE (normalized, len, *b);
27763c0ef4a0763e579c9c80887bbfbd2651de05067Behdad Esfahbod
278498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod    /* Here's the ugly part: if ab decomposes to a single character and
279498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod     * that character decomposes again, we have to detect that and undo
280498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod     * the second part :-(. */
281498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod    UChar recomposed[20];
282498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod    icu_err = U_ZERO_ERROR;
28363c0ef4a0763e579c9c80887bbfbd2651de05067Behdad Esfahbod    unorm_normalize (normalized, len, UNORM_NFC, 0, recomposed, ARRAY_LENGTH (recomposed), &icu_err);
284889caa52fa1bef61013ec1d127f84d7d5907ef1eBehdad Esfahbod    if (U_FAILURE (icu_err))
2850594a2448440208efa0acac9a5d8d52d43108289Behdad Esfahbod      return false;
28663c0ef4a0763e579c9c80887bbfbd2651de05067Behdad Esfahbod    hb_codepoint_t c;
28763c0ef4a0763e579c9c80887bbfbd2651de05067Behdad Esfahbod    U16_GET_UNSAFE (recomposed, 0, c);
28863c0ef4a0763e579c9c80887bbfbd2651de05067Behdad Esfahbod    if (c != *a && c != ab) {
28963c0ef4a0763e579c9c80887bbfbd2651de05067Behdad Esfahbod      *a = c;
290498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod      *b = 0;
291498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod    }
2920594a2448440208efa0acac9a5d8d52d43108289Behdad Esfahbod    ret = true;
293498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod  } else {
294498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod    /* If decomposed to more than two characters, take the last one,
295498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod     * and recompose the rest to get the first component. */
296a18280a8ce9128fc9d75f8a367ae8ce0886a9599Behdad Esfahbod    U16_PREV_UNSAFE (normalized, len, *b); /* Changes len in-place. */
297a18280a8ce9128fc9d75f8a367ae8ce0886a9599Behdad Esfahbod    UChar recomposed[18 * 2];
298498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod    icu_err = U_ZERO_ERROR;
299498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod    len = unorm_normalize (normalized, len, UNORM_NFC, 0, recomposed, ARRAY_LENGTH (recomposed), &icu_err);
300889caa52fa1bef61013ec1d127f84d7d5907ef1eBehdad Esfahbod    if (U_FAILURE (icu_err))
3010594a2448440208efa0acac9a5d8d52d43108289Behdad Esfahbod      return false;
302498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod    /* We expect that recomposed has exactly one character now. */
303a18280a8ce9128fc9d75f8a367ae8ce0886a9599Behdad Esfahbod    if (unlikely (u_countChar32 (recomposed, len) != 1))
304a18280a8ce9128fc9d75f8a367ae8ce0886a9599Behdad Esfahbod      return false;
305498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod    U16_GET_UNSAFE (recomposed, 0, *a);
3060594a2448440208efa0acac9a5d8d52d43108289Behdad Esfahbod    ret = true;
307498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod  }
308498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod
309498e1a9be673bb02c00aac3f12bb4c6993a85910Behdad Esfahbod  return ret;
310fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbod}
311fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbod
312378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbodstatic unsigned int
313378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbodhb_icu_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs HB_UNUSED,
314378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod					hb_codepoint_t      u,
315378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod					hb_codepoint_t     *decomposed,
316378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod					void               *user_data HB_UNUSED)
317378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod{
318378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod  UChar utf16[2], normalized[2 * HB_UNICODE_MAX_DECOMPOSITION_LEN + 1];
319b1914b8bd08ecdea79930dda7e3bb2ae9e6134a1Behdad Esfahbod  unsigned int len;
320378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod  int32_t utf32_len;
321378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod  hb_bool_t err;
322378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod  UErrorCode icu_err;
323378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod
324378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod  /* Copy @u into a UTF-16 array to be passed to ICU. */
325378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod  len = 0;
326378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod  err = FALSE;
327378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod  U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), u, err);
328378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod  if (err)
329378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod    return 0;
330378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod
331378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod  /* Normalise the codepoint using NFKD mode. */
332378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod  icu_err = U_ZERO_ERROR;
333378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod  len = unorm_normalize (utf16, len, UNORM_NFKD, 0, normalized, ARRAY_LENGTH (normalized), &icu_err);
334378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod  if (icu_err)
335378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod    return 0;
336378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod
337378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod  /* Convert the decomposed form from UTF-16 to UTF-32. */
338378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod  icu_err = U_ZERO_ERROR;
339378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod  u_strToUTF32 ((UChar32*) decomposed, HB_UNICODE_MAX_DECOMPOSITION_LEN, &utf32_len, normalized, len, &icu_err);
340378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod  if (icu_err)
341378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod    return 0;
342378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod
343378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod  return utf32_len;
344378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod}
345378d279bbf692195c4654e312dae854ab3be04cfBehdad Esfahbod
346fca368c4682624346a0aaee690e1ad6ed4c0b337Behdad Esfahbod
347d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbodhb_unicode_funcs_t *
348d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbodhb_icu_get_unicode_funcs (void)
349d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod{
350d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod  static const hb_unicode_funcs_t _hb_icu_unicode_funcs = {
351d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod    HB_OBJECT_HEADER_STATIC,
352be4560a3b5e8599cbe2b29a01a60c21c9e2b194fBehdad Esfahbod
353d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod    NULL, /* parent */
354d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod    true, /* immutable */
355d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod    {
356fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbod#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_icu_unicode_##name,
357d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod      HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
358fca0923b04aeff9369849da97d247a647611f346Behdad Esfahbod#undef HB_UNICODE_FUNC_IMPLEMENT
359d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod    }
360d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod  };
361d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod
362d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod#if U_ICU_VERSION_MAJOR_NUM >= 49
363d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod  if (!hb_atomic_ptr_get (&normalizer)) {
364d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod    UErrorCode icu_err = U_ZERO_ERROR;
365d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod    /* We ignore failure in getNFCInstace(). */
366d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod    hb_atomic_ptr_cmpexch (&normalizer, NULL, unorm2_getNFCInstance (&icu_err));
367d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod  }
368d5045a5f4017631a4660f985fe451c5a64c42ca0Behdad Esfahbod#endif
369f06ab8a4262c759b4723614fd28f55ee77aa8466Behdad Esfahbod  return const_cast<hb_unicode_funcs_t *> (&_hb_icu_unicode_funcs);
370d94647e2cd187bf4a4c8fb1c0c15c3d23c1293acBehdad Esfahbod}
371acdba3f90b232fc12fcb200dca2584481b339118Behdad Esfahbod
372acdba3f90b232fc12fcb200dca2584481b339118Behdad Esfahbod
373