evil-qemacs/unicode_join.c

366 lines
9.9 KiB
C
Raw Normal View History

2004-05-29 10:17:37 +00:00
/*
* Unicode joining algorithms for QEmacs.
2004-05-29 10:17:37 +00:00
*
* Copyright (c) 2000 Fabrice Bellard.
* Copyright (c) 2000-2023 Charlie Gordon.
2004-05-29 10:17:37 +00:00
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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
*/
2007-12-03 16:12:40 +00:00
#include "util.h"
#include "unicode_join.h"
/*---- ligature tables ----*/
2004-05-29 10:17:37 +00:00
static unsigned short *subst1;
static unsigned short *ligature2;
static unsigned short *ligature_long;
static unsigned short subst1_count;
static unsigned short ligature2_count;
2004-05-29 10:17:37 +00:00
static int uni_get_be16(FILE *f, unsigned short *pv) {
/* read a big-endiann unsigned 16-bit value from `f` into `*pv` */
/* return -1 if end of file */
int c1, c2;
if ((c1 = fgetc(f)) != EOF && (c2 = fgetc(f)) != EOF) {
*pv = (c1 << 8) | c2;
return 0;
} else {
return 1;
}
2004-05-29 10:17:37 +00:00
}
static unsigned short *read_array_be16(FILE *f, int n) {
2004-05-29 10:17:37 +00:00
unsigned short *tab;
int i;
tab = qe_malloc_array(unsigned short, n);
if (!tab)
2004-05-29 10:17:37 +00:00
return NULL;
2005-05-11 12:04:30 +00:00
for (i = 0; i < n; i++) {
if (uni_get_be16(f, &tab[i])) {
qe_free(&tab);
return NULL;
}
2004-05-29 10:17:37 +00:00
}
return tab;
}
int load_ligatures(const char *filename) {
2004-05-29 10:17:37 +00:00
FILE *f;
2007-12-12 08:51:02 +00:00
unsigned char sig[4];
unsigned short long_count;
2004-05-29 10:17:37 +00:00
2005-05-11 12:04:30 +00:00
f = fopen(filename, "r");
2004-05-29 10:17:37 +00:00
if (!f)
2007-12-18 12:12:28 +00:00
return -1;
2004-05-29 10:17:37 +00:00
if (fread(sig, 1, 4, f) != 4
|| memcmp(sig, "liga", 4) != 0
|| uni_get_be16(f, &subst1_count)
|| uni_get_be16(f, &ligature2_count)
|| uni_get_be16(f, &long_count)
|| !(subst1 = read_array_be16(f, subst1_count * 2))
|| !(ligature2 = read_array_be16(f, ligature2_count * 3))
|| !(ligature_long = read_array_be16(f, long_count))) {
unload_ligatures();
fclose(f);
return -1;
} else {
fclose(f);
return 0;
}
}
2004-05-29 10:17:37 +00:00
void unload_ligatures(void) {
qe_free(&subst1);
qe_free(&ligature2);
qe_free(&ligature_long);
2004-05-29 10:17:37 +00:00
subst1_count = 0;
ligature2_count = 0;
}
static char32_t find_ligature(char32_t l1, char32_t l2) {
int a, b, m;
char32_t v1, v2;
2004-05-29 10:17:37 +00:00
a = 0;
b = ligature2_count;
while (a < b) {
2004-05-29 10:17:37 +00:00
m = (a + b) >> 1;
v1 = ligature2[3 * m];
v2 = ligature2[3 * m + 1];
if (v1 == l1 && v2 == l2)
return ligature2[3 * m + 2];
2007-12-12 08:51:02 +00:00
else
if (v1 > l1 || (v1 == l1 && v2 > l2)) {
b = m;
2004-05-29 10:17:37 +00:00
} else {
a = m + 1;
}
}
return 0xffffffff;
2004-05-29 10:17:37 +00:00
}
int combine_accent(char32_t *buf, char32_t c, char32_t accent) {
char32_t lig = find_ligature(c, accent);
if (lig != 0xffffffff) {
*buf = lig;
return 1;
} else {
return 0;
}
}
/* No need for efficiency */
int expand_ligature(char32_t *buf, char32_t c) {
unsigned short *a, *b;
if (c > 0x7f) {
for (a = ligature2, b = a + 3 * ligature2_count; a < b; a++) {
if (a[2] == c) {
buf[0] = a[0];
buf[1] = a[1];
return 1;
}
}
}
return 0;
}
char32_t qe_unaccent(char32_t c) {
char32_t buf[2];
if (expand_ligature(buf, c) && qe_isaccent(buf[1]))
return buf[0];
else
return c;
}
/* simplistic case change for non ASCII glyphs: only support accents */
char32_t qe_wctoupper(char32_t c) {
char32_t buf[2];
if (expand_ligature(buf, c) && qe_isaccent(buf[1])){
char32_t c2 = find_ligature(qe_wtoupper(buf[0]), buf[1]);
if (c2 != 0xffffffff)
return c2;
}
return c;
}
char32_t qe_wctolower(char32_t c) {
char32_t buf[2];
if (expand_ligature(buf, c) && qe_isaccent(buf[1])) {
char32_t c2 = find_ligature(qe_wtolower(buf[0]), buf[1]);
if (c2 != 0xffffffff)
return c2;
}
return c;
}
2004-05-29 10:17:37 +00:00
/* apply all the ligature rules in logical order. Always return a
smaller or equal buffer */
static int unicode_ligature(char32_t *buf_out,
2004-05-29 10:17:37 +00:00
unsigned int *pos_L_to_V,
int len)
{
int len1, len2, i, j;
char32_t l, l1, l2;
char32_t *q;
2004-05-29 10:17:37 +00:00
const unsigned short *lig;
/* CG: C99 variable-length arrays may be too large */
// XXX: why do we need a local copy?
char32_t buf[len];
blockcpy(buf, buf_out, len);
2004-05-29 10:17:37 +00:00
q = buf_out;
2005-05-11 12:04:30 +00:00
for (i = 0; i < len;) {
2004-05-29 10:17:37 +00:00
l1 = buf[i];
/* eliminate invisible chars */
if (l1 >= 0x202a && l1 <= 0x202e) {
/* LRE, RLE, PDF, RLO, LRO */
pos_L_to_V[i] = q - buf_out;
i++;
goto found;
}
/* fast test to eliminate common cases */
if (i == len - 1)
2004-05-29 10:17:37 +00:00
goto nolig;
l2 = buf[i + 1];
if (l1 <= 0x7f && l2 <= 0x7f)
goto nolig;
l = find_ligature(l1, l2);
if (l == 0xffffffff)
2004-05-29 10:17:37 +00:00
goto nolig;
if (l > 0) {
/* ligature of length 2 found */
pos_L_to_V[i] = q - buf_out;
pos_L_to_V[i+1] = q - buf_out;
*q++ = l;
i += 2;
} else {
2007-12-12 08:51:02 +00:00
/* generic case: use ligature_long[] table */
2004-05-29 10:17:37 +00:00
lig = ligature_long;
2005-05-11 12:04:30 +00:00
for (;;) {
2004-05-29 10:17:37 +00:00
len1 = *lig++;
if (len1 == 0)
break;
len2 = *lig++;
if (i + len1 <= len) {
2005-05-11 12:04:30 +00:00
for (j = 0; j < len1; j++) {
2004-05-29 10:17:37 +00:00
if (buf[i+j] != lig[j])
goto notfound;
}
2005-05-11 12:04:30 +00:00
for (j = 0; j < len1; j++)
2004-05-29 10:17:37 +00:00
pos_L_to_V[i + j] = q - buf_out;
2005-05-11 12:04:30 +00:00
for (j = 0; j < len2; j++) {
2004-05-29 10:17:37 +00:00
*q++ = lig[len1 + j];
}
i += len1;
goto found;
}
notfound:
lig += len1 + len2;
}
/* nothing found */
nolig:
pos_L_to_V[i] = q - buf_out;
*q++ = l1;
i++;
found:
2004-11-30 10:18:47 +00:00
;
2004-05-29 10:17:37 +00:00
}
}
return q - buf_out;
}
/* fast classification of unicode chars to optimize the algorithms */
#define UNICODE_ARABIC 0x00000001
#define UNICODE_INDIC 0x00000002
#define UNICODE_NONASCII 0x00000004
static int unicode_classify(const char32_t *buf, int len) {
int i, mask;
2004-05-29 10:17:37 +00:00
mask = 0;
2005-05-11 12:04:30 +00:00
for (i = 0; i < len; i++) {
char32_t c = buf[i];
2004-05-29 10:17:37 +00:00
if (c <= 0x7f) /* latin1 fast handling */
continue;
mask |= UNICODE_NONASCII;
if (c < 0xA00) {
if ((c & ~0xff) == 0x600) /* 0600..06FF */
mask |= UNICODE_ARABIC;
else
if ((c & ~0x7f) == 0x900) /* 0900..097F */
mask |= UNICODE_INDIC;
}
2004-05-29 10:17:37 +00:00
}
return mask;
}
static void compose_char_to_glyph(unsigned int *ctog, int len,
const unsigned int *ctog1)
2004-05-29 10:17:37 +00:00
{
int i;
2005-05-11 12:04:30 +00:00
for (i = 0; i < len; i++)
2004-05-29 10:17:37 +00:00
ctog[i] = ctog1[ctog[i]];
}
static void bidi_reverse_buf(char32_t *str, int len)
2004-05-29 10:17:37 +00:00
{
int i, len2 = len / 2;
2004-05-29 10:17:37 +00:00
for (i = 0; i < len2; i++) {
char32_t tmp = str[i];
2005-05-11 12:04:30 +00:00
str[i] = fribidi_get_mirror_char(str[len - 1 - i]);
str[len - 1 - i] = fribidi_get_mirror_char(tmp);
2004-05-29 10:17:37 +00:00
}
2007-12-12 08:51:02 +00:00
/* do not forget central char! */
2004-05-29 10:17:37 +00:00
if (len & 1) {
str[len2] = fribidi_get_mirror_char(str[len2]);
}
}
/* Convert a string of unicode characters to a string of glyphs. We
assume that the font implements a minimum number of standard
ligature chars. The string is reversed if 'reversed' is set to
2004-05-29 10:17:37 +00:00
deal with the bidir case. 'char_to_glyph_pos' gives the index of
the first glyph associated to a given character of the source
buffer. */
int unicode_to_glyphs(char32_t *dst, unsigned int *char_to_glyph_pos,
int dst_size, char32_t *src, int src_size,
2005-05-11 12:04:30 +00:00
int reverse)
2004-05-29 10:17:37 +00:00
{
int len, i;
/* CG: C99 variable-length arrays may be too large */
2004-05-29 10:17:37 +00:00
unsigned int ctog[src_size];
unsigned int ctog1[src_size];
char32_t buf[src_size];
2004-05-29 10:17:37 +00:00
int unicode_class;
unicode_class = unicode_classify(src, src_size);
if (unicode_class == 0 && !reverse) {
2007-12-12 08:51:02 +00:00
/* fast case: no special treatment */
len = min_int(src_size, dst_size);
blockcpy(dst, src, len);
2004-05-29 10:17:37 +00:00
if (char_to_glyph_pos) {
2005-05-04 19:27:36 +00:00
for (i = 0; i < len; i++)
2004-05-29 10:17:37 +00:00
char_to_glyph_pos[i] = i;
}
return len;
} else {
/* generic case */
/* init current buffer */
len = src_size;
2005-05-04 19:27:36 +00:00
for (i = 0; i < len; i++)
2004-05-29 10:17:37 +00:00
ctog[i] = i;
blockcpy(buf, src, len);
2004-05-29 10:17:37 +00:00
/* apply each filter */
2004-05-29 10:17:37 +00:00
if (unicode_class & UNICODE_ARABIC) {
len = arabic_join(buf, ctog1, len);
/* not needed for arabic_join */
2004-05-29 10:17:37 +00:00
//compose_char_to_glyph(ctog, src_size, ctog1);
}
if (unicode_class & UNICODE_INDIC) {
len = devanagari_log2vis(buf, ctog1, len);
compose_char_to_glyph(ctog, src_size, ctog1);
}
2004-05-29 10:17:37 +00:00
len = unicode_ligature(buf, ctog1, len);
compose_char_to_glyph(ctog, src_size, ctog1);
if (reverse) {
bidi_reverse_buf(buf, len);
2005-05-11 12:04:30 +00:00
for (i = 0; i < src_size; i++) {
2004-05-29 10:17:37 +00:00
ctog[i] = len - 1 - ctog[i];
}
}
if (len > dst_size)
len = dst_size;
blockcpy(dst, buf, len);
2004-05-29 10:17:37 +00:00
if (char_to_glyph_pos) {
blockcpy(char_to_glyph_pos, ctog, src_size);
2004-05-29 10:17:37 +00:00
}
}
return len;
}