Coverage Report

Created: 2026-08-13 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fribidi/lib/fribidi-arabic.c
Line
Count
Source
1
/* fribidi-arabic.c - Arabic shaping
2
 *
3
 * Copyright (C) 2005  Behdad Esfahbod
4
 *
5
 * This file is part of GNU FriBidi.
6
 * 
7
 * GNU FriBidi is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public License
9
 * as published by the Free Software Foundation; either version 2.1
10
 * of the License, or (at your option) any later version.
11
 * 
12
 * GNU FriBidi is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Lesser General Public License for more details.
16
 * 
17
 * You should have received a copy of the GNU Lesser General Public License
18
 * along with GNU FriBidi; if not, see: <https://www.gnu.org/licenses/>.
19
 * 
20
 * For licensing issues, contact <fribidi.license@gmail.com> or write to
21
 * Sharif FarsiWeb, Inc., PO Box 13445-389, Tehran, Iran.
22
 *
23
 * Author(s):
24
 *   Behdad Esfahbod, 2005
25
 */
26
27
#include "common.h"
28
29
#ifdef HAVE_CONFIG_H
30
# include <config.h>
31
#endif
32
33
#ifdef HAVE_STDLIB_H
34
# include <stdlib.h>
35
#endif
36
37
38
#include <fribidi-arabic.h>
39
#include <fribidi-unicode.h>
40
41
42
typedef struct _PairMap {
43
  FriBidiChar pair[2], to;
44
} PairMap;
45
46
47
0
#define FRIBIDI_ACCESS_SHAPE_TABLE(table,min,max,x,shape) (table), (min), (max)
48
# define FRIBIDI_ACCESS_SHAPE_TABLE_REAL(table,min,max,x,shape) \
49
0
  (((x)<(min)||(x)>(max))?(x):(table)[(x)-(min)][(shape)])
50
51
#include "arabic-shaping.tab.i"
52
#include "arabic-misc.tab.i"
53
54
55
static void
56
fribidi_shape_arabic_joining (
57
  /* input */
58
  const FriBidiChar table[][4],
59
  FriBidiChar min,
60
  FriBidiChar max,
61
  const FriBidiStrIndex len,
62
  const FriBidiArabicProp *ar_props,
63
  /* input and output */
64
  FriBidiChar *str
65
)
66
0
{
67
0
  register FriBidiStrIndex i;
68
69
0
  for (i = 0; i < len; i++)
70
0
    if (FRIBIDI_ARAB_SHAPES(ar_props[i]))
71
0
      str[i] = FRIBIDI_ACCESS_SHAPE_TABLE_REAL (table, min, max, str[i], FRIBIDI_JOIN_SHAPE (ar_props[i]));
72
0
}
73
74
75
76
static int
77
comp_PairMap (const void *pa, const void *pb)
78
0
{
79
0
  PairMap *a = (PairMap *)pa;
80
0
  PairMap *b = (PairMap *)pb;
81
82
0
  if (a->pair[0] != b->pair[0])
83
0
    return a->pair[0] < b->pair[0] ? -1 : +1;
84
0
  else
85
0
    return a->pair[1] < b->pair[1] ? -1 :
86
0
           a->pair[1] > b->pair[1] ? +1 :
87
0
     0;
88
0
}
89
90
static void *
91
fribidi_bsearch (const void *key, const void *base,
92
                 unsigned int nmemb, unsigned int size,
93
                 int (*compar)(const void *_key, const void *_item))
94
0
{
95
0
  int min = 0, max = (int) nmemb - 1;
96
0
  while (min <= max)
97
0
  {
98
0
    int mid = ((unsigned int) min + (unsigned int) max) / 2;
99
0
    const void *p = (const void *) (((const char *) base) + (mid * size));
100
0
    int c = compar (key, p);
101
0
    if (c < 0)
102
0
      max = mid - 1;
103
0
    else if (c > 0)
104
0
      min = mid + 1;
105
0
    else
106
0
      return (void *) p;
107
0
  }
108
0
  return NULL;
109
0
}
110
111
static FriBidiChar
112
find_pair_match (const PairMap *table, int size, FriBidiChar first, FriBidiChar second)
113
0
{
114
0
  PairMap *match;
115
0
  PairMap x;
116
0
  x.pair[0] = first;
117
0
  x.pair[1] = second;
118
0
  x.to = 0;
119
0
  match = fribidi_bsearch (&x, table, size, sizeof (table[0]), comp_PairMap);
120
0
  return match ? match->to : 0;
121
0
}
122
123
#define PAIR_MATCH(table,len,first,second) \
124
0
  ((first)<(table[0].pair[0])||(first)>(table[len-1].pair[0])?0: \
125
0
   find_pair_match(table, len, first, second))
126
127
static void
128
fribidi_shape_arabic_ligature (
129
  /* input */
130
  const PairMap *table,
131
  int size,
132
  const FriBidiLevel *embedding_levels,
133
  const FriBidiStrIndex len,
134
  /* input and output */
135
  FriBidiArabicProp *ar_props,
136
  FriBidiChar *str
137
)
138
0
{
139
  /* TODO: This doesn't form ligatures for even-level Arabic text.
140
   * no big problem though. */
141
0
  register FriBidiStrIndex i;
142
143
0
  for (i = 0; i < len - 1; i++) {
144
0
    register FriBidiChar c;
145
0
    if (FRIBIDI_LEVEL_IS_RTL(embedding_levels[i]) &&
146
0
  embedding_levels[i] == embedding_levels[i+1] &&
147
0
  (c = PAIR_MATCH(table, size, str[i], str[i+1])))
148
0
      {
149
0
  str[i] = FRIBIDI_CHAR_FILL;
150
0
  FRIBIDI_SET_BITS(ar_props[i], FRIBIDI_MASK_LIGATURED);
151
0
  str[i+1] = c;
152
0
      }
153
0
  }
154
0
}
155
156
#define DO_LIGATURING(table, levels, len, ar_props, str) \
157
0
  fribidi_shape_arabic_ligature ((table), sizeof(table)/sizeof((table)[0]), levels, len, ar_props, str)
158
159
#define DO_SHAPING(tablemacro, len, ar_props, str) \
160
0
  fribidi_shape_arabic_joining (tablemacro(,), len, ar_props, str);
161
  
162
163
164
165
FRIBIDI_ENTRY void
166
fribidi_shape_arabic (
167
  /* input */
168
  FriBidiFlags flags,
169
  const FriBidiLevel *embedding_levels,
170
  const FriBidiStrIndex len,
171
  /* input and output */
172
  FriBidiArabicProp *ar_props,
173
  FriBidiChar *str
174
)
175
0
{
176
0
  DBG ("in fribidi_shape_arabic");
177
178
0
  if UNLIKELY
179
0
    (len == 0 || !str) return;
180
181
0
  DBG ("in fribidi_shape");
182
183
0
  fribidi_assert (ar_props);
184
185
0
  if (FRIBIDI_TEST_BITS (flags, FRIBIDI_FLAG_SHAPE_ARAB_PRES))
186
0
    {
187
0
      DO_SHAPING (FRIBIDI_GET_ARABIC_SHAPE_PRES, len, ar_props, str);
188
0
    }
189
190
0
  if (FRIBIDI_TEST_BITS (flags, FRIBIDI_FLAG_SHAPE_ARAB_LIGA))
191
0
    {
192
0
      DO_LIGATURING (mandatory_liga_table, embedding_levels, len, ar_props, str);
193
0
    }
194
195
0
  if (FRIBIDI_TEST_BITS (flags, FRIBIDI_FLAG_SHAPE_ARAB_CONSOLE))
196
0
    {
197
0
      DO_LIGATURING (console_liga_table, embedding_levels, len, ar_props, str);
198
0
      DO_SHAPING (FRIBIDI_GET_ARABIC_SHAPE_NSM, len, ar_props, str);
199
0
    }
200
0
}
201
202
/* Editor directions:
203
 * Local Variables:
204
 *   mode: c
205
 *   c-basic-offset: 2
206
 *   indent-tabs-mode: t
207
 *   tab-width: 8
208
 * End:
209
 * vim: textwidth=78: autoindent: cindent: shiftwidth=2: tabstop=8:
210
 */