2013-11-18 09:38:53 +00:00
|
|
|
#!/usr/bin/env python3
|
2023-08-15 14:20:26 +00:00
|
|
|
# SPDX-FileCopyrightText: 2012-2023 Blender Authors
|
2023-06-15 03:09:04 +00:00
|
|
|
#
|
2022-02-10 22:07:11 +00:00
|
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
2012-07-02 19:51:06 +00:00
|
|
|
|
2019-08-11 12:41:04 +00:00
|
|
|
# Pre-process right-to-left languages.
|
2012-07-02 19:51:06 +00:00
|
|
|
# You can use it either standalone, or through import_po_from_branches or
|
|
|
|
# update_trunk.
|
|
|
|
#
|
|
|
|
# Notes: This has been tested on Linux, not 100% it will work nicely on
|
|
|
|
# Windows or OsX.
|
|
|
|
# This uses ctypes, as there is no py3 binding for fribidi currently.
|
|
|
|
# This implies you only need the compiled C library to run it.
|
2018-09-03 14:49:08 +00:00
|
|
|
# Finally, note that it handles some formatting/escape codes (like
|
2012-07-02 19:51:06 +00:00
|
|
|
# \", %s, %x12, %.4f, etc.), protecting them from ugly (evil) fribidi,
|
|
|
|
# which seems completely unaware of such things (as unicode is...).
|
|
|
|
|
|
|
|
import ctypes
|
2012-10-04 13:49:39 +00:00
|
|
|
import re
|
2012-07-02 19:51:06 +00:00
|
|
|
|
|
|
|
|
2019-09-09 20:11:52 +00:00
|
|
|
# define FRIBIDI_MASK_NEUTRAL 0x00000040L /* Is neutral */
|
2012-07-02 19:51:06 +00:00
|
|
|
FRIBIDI_PAR_ON = 0x00000040
|
|
|
|
|
|
|
|
|
2019-09-09 20:11:52 +00:00
|
|
|
# define FRIBIDI_FLAG_SHAPE_MIRRORING 0x00000001
|
|
|
|
# define FRIBIDI_FLAG_REORDER_NSM 0x00000002
|
2012-07-02 19:51:06 +00:00
|
|
|
|
2019-09-09 20:11:52 +00:00
|
|
|
# define FRIBIDI_FLAG_SHAPE_ARAB_PRES 0x00000100
|
|
|
|
# define FRIBIDI_FLAG_SHAPE_ARAB_LIGA 0x00000200
|
|
|
|
# define FRIBIDI_FLAG_SHAPE_ARAB_CONSOLE 0x00000400
|
2012-07-02 19:51:06 +00:00
|
|
|
|
2019-09-09 20:11:52 +00:00
|
|
|
# define FRIBIDI_FLAG_REMOVE_BIDI 0x00010000
|
|
|
|
# define FRIBIDI_FLAG_REMOVE_JOINING 0x00020000
|
|
|
|
# define FRIBIDI_FLAG_REMOVE_SPECIALS 0x00040000
|
2012-07-02 19:51:06 +00:00
|
|
|
|
2019-09-09 20:11:52 +00:00
|
|
|
# define FRIBIDI_FLAGS_DEFAULT ( \
|
|
|
|
# FRIBIDI_FLAG_SHAPE_MIRRORING | \
|
|
|
|
# FRIBIDI_FLAG_REORDER_NSM | \
|
|
|
|
# FRIBIDI_FLAG_REMOVE_SPECIALS )
|
2012-07-02 19:51:06 +00:00
|
|
|
|
2019-09-09 20:11:52 +00:00
|
|
|
# define FRIBIDI_FLAGS_ARABIC ( \
|
|
|
|
# FRIBIDI_FLAG_SHAPE_ARAB_PRES | \
|
|
|
|
# FRIBIDI_FLAG_SHAPE_ARAB_LIGA )
|
2012-07-02 19:51:06 +00:00
|
|
|
|
|
|
|
FRIBIDI_FLAG_SHAPE_MIRRORING = 0x00000001
|
|
|
|
FRIBIDI_FLAG_REORDER_NSM = 0x00000002
|
|
|
|
FRIBIDI_FLAG_REMOVE_SPECIALS = 0x00040000
|
|
|
|
|
|
|
|
FRIBIDI_FLAG_SHAPE_ARAB_PRES = 0x00000100
|
|
|
|
FRIBIDI_FLAG_SHAPE_ARAB_LIGA = 0x00000200
|
|
|
|
|
2013-02-24 08:50:55 +00:00
|
|
|
FRIBIDI_FLAGS_DEFAULT = FRIBIDI_FLAG_SHAPE_MIRRORING | FRIBIDI_FLAG_REORDER_NSM | FRIBIDI_FLAG_REMOVE_SPECIALS
|
2012-07-02 19:51:06 +00:00
|
|
|
|
2013-02-24 08:50:55 +00:00
|
|
|
FRIBIDI_FLAGS_ARABIC = FRIBIDI_FLAG_SHAPE_ARAB_PRES | FRIBIDI_FLAG_SHAPE_ARAB_LIGA
|
2012-07-02 19:51:06 +00:00
|
|
|
|
2012-07-29 12:07:06 +00:00
|
|
|
|
2012-10-04 13:49:39 +00:00
|
|
|
MENU_DETECT_REGEX = re.compile("%x\\d+\\|")
|
|
|
|
|
|
|
|
|
2012-07-02 19:51:06 +00:00
|
|
|
##### Kernel processing funcs. #####
|
|
|
|
def protect_format_seq(msg):
|
|
|
|
"""
|
2018-09-03 14:49:08 +00:00
|
|
|
Find some specific escaping/formatting sequences (like \", %s, etc.,
|
2012-07-02 19:51:06 +00:00
|
|
|
and protect them from any modification!
|
|
|
|
"""
|
2012-10-04 13:49:39 +00:00
|
|
|
# LRM = "\u200E"
|
|
|
|
# RLM = "\u200F"
|
2012-07-02 19:51:06 +00:00
|
|
|
LRE = "\u202A"
|
2021-06-15 00:44:08 +00:00
|
|
|
# RLE = "\u202B"
|
2012-07-02 19:51:06 +00:00
|
|
|
PDF = "\u202C"
|
2012-10-04 13:49:39 +00:00
|
|
|
LRO = "\u202D"
|
2021-06-15 00:44:08 +00:00
|
|
|
# RLO = "\u202E"
|
2021-03-06 07:16:39 +00:00
|
|
|
# uctrl = {LRE, RLE, PDF, LRO, RLO}
|
2012-07-02 19:51:06 +00:00
|
|
|
# Most likely incomplete, but seems to cover current needs.
|
|
|
|
format_codes = set("tslfd")
|
|
|
|
digits = set(".0123456789")
|
|
|
|
|
2012-10-04 13:49:39 +00:00
|
|
|
if not msg:
|
|
|
|
return msg
|
|
|
|
elif MENU_DETECT_REGEX.search(msg):
|
|
|
|
# An ugly "menu" message, just force it whole LRE if not yet done.
|
|
|
|
if msg[0] not in {LRE, LRO}:
|
|
|
|
msg = LRE + msg
|
|
|
|
|
2012-07-02 19:51:06 +00:00
|
|
|
idx = 0
|
|
|
|
ret = []
|
|
|
|
ln = len(msg)
|
|
|
|
while idx < ln:
|
|
|
|
dlt = 1
|
2012-10-04 13:49:39 +00:00
|
|
|
# # If we find a control char, skip any additional protection!
|
|
|
|
# if msg[idx] in uctrl:
|
|
|
|
# ret.append(msg[idx:])
|
|
|
|
# break
|
2012-07-02 19:51:06 +00:00
|
|
|
# \" or \'
|
|
|
|
if idx < (ln - 1) and msg[idx] == '\\' and msg[idx + 1] in "\"\'":
|
|
|
|
dlt = 2
|
2012-10-04 13:49:39 +00:00
|
|
|
# %x12|
|
|
|
|
elif idx < (ln - 2) and msg[idx] == '%' and msg[idx + 1] in "x" and msg[idx + 2] in digits:
|
2012-07-02 19:51:06 +00:00
|
|
|
dlt = 2
|
2012-10-04 13:49:39 +00:00
|
|
|
while (idx + dlt) < ln and msg[idx + dlt] in digits:
|
|
|
|
dlt += 1
|
2018-12-19 00:38:20 +00:00
|
|
|
if (idx + dlt) < ln and msg[idx + dlt] == '|':
|
2012-07-02 19:51:06 +00:00
|
|
|
dlt += 1
|
|
|
|
# %.4f
|
|
|
|
elif idx < (ln - 3) and msg[idx] == '%' and msg[idx + 1] in digits:
|
|
|
|
dlt = 2
|
2012-10-04 13:49:39 +00:00
|
|
|
while (idx + dlt) < ln and msg[idx + dlt] in digits:
|
2012-07-02 19:51:06 +00:00
|
|
|
dlt += 1
|
2012-10-04 13:49:39 +00:00
|
|
|
if (idx + dlt) < ln and msg[idx + dlt] in format_codes:
|
2012-07-02 19:51:06 +00:00
|
|
|
dlt += 1
|
|
|
|
else:
|
|
|
|
dlt = 1
|
|
|
|
# %s
|
2012-10-04 13:49:39 +00:00
|
|
|
elif idx < (ln - 1) and msg[idx] == '%' and msg[idx + 1] in format_codes:
|
2012-07-02 19:51:06 +00:00
|
|
|
dlt = 2
|
|
|
|
|
|
|
|
if dlt > 1:
|
|
|
|
ret.append(LRE)
|
|
|
|
ret += msg[idx:idx + dlt]
|
|
|
|
idx += dlt
|
|
|
|
if dlt > 1:
|
|
|
|
ret.append(PDF)
|
|
|
|
|
|
|
|
return "".join(ret)
|
|
|
|
|
|
|
|
|
2013-02-24 08:50:55 +00:00
|
|
|
def log2vis(msgs, settings):
|
2012-07-02 19:51:06 +00:00
|
|
|
"""
|
|
|
|
Globally mimics deprecated fribidi_log2vis.
|
2023-09-05 00:49:20 +00:00
|
|
|
msgs should be an iterable of messages to RTL-process.
|
2012-07-02 19:51:06 +00:00
|
|
|
"""
|
2013-02-24 08:50:55 +00:00
|
|
|
fbd = ctypes.CDLL(settings.FRIBIDI_LIB)
|
|
|
|
|
2012-07-02 19:51:06 +00:00
|
|
|
for msg in msgs:
|
|
|
|
msg = protect_format_seq(msg)
|
|
|
|
|
|
|
|
fbc_str = ctypes.create_unicode_buffer(msg)
|
|
|
|
ln = len(fbc_str) - 1
|
|
|
|
# print(fbc_str.value, ln)
|
|
|
|
btypes = (ctypes.c_int * ln)()
|
|
|
|
embed_lvl = (ctypes.c_uint8 * ln)()
|
|
|
|
pbase_dir = ctypes.c_int(FRIBIDI_PAR_ON)
|
|
|
|
jtypes = (ctypes.c_uint8 * ln)()
|
|
|
|
flags = FRIBIDI_FLAGS_DEFAULT | FRIBIDI_FLAGS_ARABIC
|
|
|
|
|
|
|
|
# Find out direction of each char.
|
|
|
|
fbd.fribidi_get_bidi_types(fbc_str, ln, ctypes.byref(btypes))
|
|
|
|
|
|
|
|
# print(*btypes)
|
|
|
|
|
|
|
|
fbd.fribidi_get_par_embedding_levels(btypes, ln,
|
|
|
|
ctypes.byref(pbase_dir),
|
|
|
|
embed_lvl)
|
|
|
|
|
|
|
|
# print(*embed_lvl)
|
|
|
|
|
|
|
|
# Joinings for arabic chars.
|
|
|
|
fbd.fribidi_get_joining_types(fbc_str, ln, jtypes)
|
|
|
|
# print(*jtypes)
|
|
|
|
fbd.fribidi_join_arabic(btypes, ln, embed_lvl, jtypes)
|
|
|
|
# print(*jtypes)
|
|
|
|
|
|
|
|
# Final Shaping!
|
|
|
|
fbd.fribidi_shape(flags, embed_lvl, ln, jtypes, fbc_str)
|
|
|
|
|
|
|
|
# print(fbc_str.value)
|
|
|
|
# print(*(ord(c) for c in fbc_str))
|
|
|
|
# And now, the reordering.
|
|
|
|
# Note that here, we expect a single line, so no need to do
|
|
|
|
# fancy things...
|
|
|
|
fbd.fribidi_reorder_line(flags, btypes, ln, 0, pbase_dir, embed_lvl,
|
|
|
|
fbc_str, None)
|
|
|
|
# print(fbc_str.value)
|
|
|
|
# print(*(ord(c) for c in fbc_str))
|
|
|
|
|
|
|
|
yield fbc_str.value
|