Merge branch 'python-pillow:main' into eps_plugin_perf

This commit is contained in:
Yay295 2023-01-12 16:03:31 -06:00 committed by GitHub
commit 89bdcfe835
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 512 additions and 110 deletions

View File

@ -5,7 +5,7 @@ on: [push, pull_request, workflow_dispatch]
permissions:
contents: read
concurrency:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
@ -49,7 +49,6 @@ jobs:
${{ matrix.package }}-python3-numpy \
${{ matrix.package }}-python3-olefile \
${{ matrix.package }}-python3-pip \
${{ matrix.package }}-python-pyqt6 \
${{ matrix.package }}-python3-setuptools \
${{ matrix.package }}-freetype \
${{ matrix.package }}-gcc \
@ -63,6 +62,11 @@ jobs:
${{ matrix.package }}-openjpeg2 \
subversion
if [ ${{ matrix.package }} == "mingw-w64-x86_64" ]; then
pacman -S --noconfirm \
${{ matrix.package }}-python-pyqt6
fi
python3 -m pip install pyroma pytest pytest-cov pytest-timeout
pushd depends && ./install_extra_test_images.sh && popd

View File

@ -5,6 +5,9 @@ Changelog (Pillow)
9.5.0 (unreleased)
------------------
- Raise ValueError for BoxBlur filter with negative radius #6874
[hugovk, radarhere]
- Support arbitrary number of loaded modules on Windows #6761
[javidcf, radarhere, nulano]

View File

@ -24,6 +24,7 @@ from .helper import assert_image_equal, hopper
ImageFilter.ModeFilter,
ImageFilter.GaussianBlur,
ImageFilter.GaussianBlur(5),
ImageFilter.BoxBlur(0),
ImageFilter.BoxBlur(5),
ImageFilter.UnsharpMask,
ImageFilter.UnsharpMask(10),
@ -173,3 +174,14 @@ def test_consistency_5x5(mode):
Image.merge(mode, source[: len(mode)]).filter(kernel),
Image.merge(mode, reference[: len(mode)]),
)
def test_invalid_box_blur_filter():
with pytest.raises(ValueError):
ImageFilter.BoxBlur(-2)
im = hopper()
box_blur_filter = ImageFilter.BoxBlur(2)
box_blur_filter.radius = -2
with pytest.raises(ValueError):
im.filter(box_blur_filter)

View File

@ -2,7 +2,7 @@
# install raqm
archive=libraqm-0.9.0
archive=libraqm-0.10.0
./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/main/$archive.tar.gz

View File

@ -85,6 +85,10 @@ Pillow for enterprise is available via the Tidelift Subscription. `Learn more <h
:target: https://fosstodon.org/@pillow
:alt: Follow on https://fosstodon.org/@pillow
.. raw:: html
<link rel="me" href="https://fosstodon.org/@pillow">
Overview
========

View File

@ -478,13 +478,13 @@ These platforms have been reported to work at the versions mentioned.
| Operating system | | Tested Python | | Latest tested | | Tested |
| | | versions | | Pillow version | | processors |
+==================================+===========================+==================+==============+
| macOS 13 Ventura | 3.7, 3.8, 3.9, 3.10, 3.11 | 9.3.0 |arm |
| macOS 13 Ventura | 3.7, 3.8, 3.9, 3.10, 3.11 | 9.4.0 |arm |
+----------------------------------+---------------------------+------------------+--------------+
| macOS 12 Big Sur | 3.7, 3.8, 3.9, 3.10, 3.11 | 9.3.0 |arm |
+----------------------------------+---------------------------+------------------+--------------+
| macOS 11 Big Sur | 3.7, 3.8, 3.9, 3.10 | 8.4.0 |arm |
| +---------------------------+------------------+--------------+
| | 3.7, 3.8, 3.9, 3.10, 3.11 | 9.3.0 |x86-64 |
| | 3.7, 3.8, 3.9, 3.10, 3.11 | 9.4.0 |x86-64 |
| +---------------------------+------------------+ |
| | 3.6 | 8.4.0 | |
+----------------------------------+---------------------------+------------------+--------------+

View File

@ -183,6 +183,9 @@ class BoxBlur(MultibandFilter):
name = "BoxBlur"
def __init__(self, radius):
if radius < 0:
msg = "radius must be >= 0"
raise ValueError(msg)
self.radius = radius
def filter(self, image):

View File

@ -237,6 +237,9 @@ ImagingBoxBlur(Imaging imOut, Imaging imIn, float radius, int n) {
if (n < 1) {
return ImagingError_ValueError("number of passes must be greater than zero");
}
if (radius < 0) {
return ImagingError_ValueError("radius must be >= 0");
}
if (strcmp(imIn->mode, imOut->mode) || imIn->type != imOut->type ||
imIn->bands != imOut->bands || imIn->xsize != imOut->xsize ||

View File

@ -11,7 +11,7 @@ It currently provides bidirectional text support (using [FriBiDi][1] or
As a result, Raqm can support most writing systems covered by Unicode.
The documentation can be accessed on the web at:
> http://host-oman.github.io/libraqm/
> https://host-oman.github.io/libraqm/
Raqm (Arabic: رَقْم) is writing, also number or digit and the Arabic word for
digital (رَقَمِيّ) shares the same root, so it is a play on “digital writing”.
@ -81,5 +81,5 @@ The following projects have patches to support complex text layout using Raqm:
[1]: https://github.com/fribidi/fribidi
[2]: https://github.com/Tehreer/SheenBidi
[3]: https://github.com/harfbuzz/harfbuzz
[4]: https://freetype.org/
[4]: https://www.freetype.org
[5]: https://www.gtk.org/gtk-doc

View File

@ -32,10 +32,10 @@
#define _RAQM_VERSION_H_
#define RAQM_VERSION_MAJOR 0
#define RAQM_VERSION_MINOR 9
#define RAQM_VERSION_MINOR 10
#define RAQM_VERSION_MICRO 0
#define RAQM_VERSION_STRING "0.9.0"
#define RAQM_VERSION_STRING "0.10.0"
#define RAQM_VERSION_ATLEAST(major,minor,micro) \
((major)*10000+(minor)*100+(micro) <= \

View File

@ -171,19 +171,23 @@
typedef FriBidiLevel _raqm_bidi_level_t;
#endif
typedef struct {
typedef struct
{
FT_Face ftface;
int ftloadflags;
hb_language_t lang;
hb_script_t script;
int spacing_after;
} _raqm_text_info;
typedef struct _raqm_run raqm_run_t;
struct _raqm {
struct _raqm
{
int ref_count;
uint32_t *text;
uint16_t *text_utf16;
char *text_utf8;
size_t text_len;
size_t text_capacity_bytes;
@ -205,7 +209,8 @@ struct _raqm {
int invisible_glyph;
};
struct _raqm_run {
struct _raqm_run
{
uint32_t pos;
uint32_t len;
@ -217,9 +222,13 @@ struct _raqm_run {
raqm_run_t *next;
};
static uint32_t
_raqm_u8_to_u32_index (raqm_t *rq,
uint32_t index);
static size_t
_raqm_encoding_to_u32_index (raqm_t *rq,
size_t index);
static bool
_raqm_allowed_grapheme_boundary (hb_codepoint_t l_char,
hb_codepoint_t r_char);
static void
_raqm_init_text_info (raqm_t *rq)
@ -231,6 +240,7 @@ _raqm_init_text_info (raqm_t *rq)
rq->text_info[i].ftloadflags = -1;
rq->text_info[i].lang = default_lang;
rq->text_info[i].script = HB_SCRIPT_INVALID;
rq->text_info[i].spacing_after = 0;
}
}
@ -263,6 +273,8 @@ _raqm_compare_text_info (_raqm_text_info a,
if (a.script != b.script)
return false;
/* Spacing shouldn't break runs, so we don't compare them here. */
return true;
}
@ -273,6 +285,7 @@ _raqm_free_text(raqm_t* rq)
rq->text = NULL;
rq->text_info = NULL;
rq->text_utf8 = NULL;
rq->text_utf16 = NULL;
rq->text_len = 0;
rq->text_capacity_bytes = 0;
}
@ -280,12 +293,15 @@ _raqm_free_text(raqm_t* rq)
static bool
_raqm_alloc_text(raqm_t *rq,
size_t len,
bool need_utf8)
bool need_utf8,
bool need_utf16)
{
/* Allocate contiguous memory block for texts and text_info */
size_t mem_size = (sizeof (uint32_t) + sizeof (_raqm_text_info)) * len;
if (need_utf8)
mem_size += sizeof (char) * len;
else if (need_utf16)
mem_size += sizeof (uint16_t) * len;
if (mem_size > rq->text_capacity_bytes)
{
@ -302,6 +318,7 @@ _raqm_alloc_text(raqm_t *rq,
rq->text_info = (_raqm_text_info*)(rq->text + len);
rq->text_utf8 = need_utf8 ? (char*)(rq->text_info + len) : NULL;
rq->text_utf16 = need_utf16 ? (uint16_t*)(rq->text_info + len) : NULL;
return true;
}
@ -357,7 +374,7 @@ _raqm_free_runs (raqm_run_t *runs)
* Return value:
* A newly allocated #raqm_t with a reference count of 1. The initial reference
* count should be released with raqm_destroy() when you are done using the
* #raqm_t. Returns %NULL in case of error.
* #raqm_t. Returns `NULL` in case of error.
*
* Since: 0.1
*/
@ -381,6 +398,7 @@ raqm_create (void)
rq->invisible_glyph = 0;
rq->text = NULL;
rq->text_utf16 = NULL;
rq->text_utf8 = NULL;
rq->text_info = NULL;
rq->text_capacity_bytes = 0;
@ -498,7 +516,7 @@ raqm_clear_contents (raqm_t *rq)
* separately can give improper output.
*
* Return value:
* %true if no errors happened, %false otherwise.
* `true` if no errors happened, `false` otherwise.
*
* Since: 0.1
*/
@ -518,7 +536,7 @@ raqm_set_text (raqm_t *rq,
if (!len)
return true;
if (!_raqm_alloc_text(rq, len, false))
if (!_raqm_alloc_text(rq, len, false, false))
return false;
rq->text_len = len;
@ -575,6 +593,53 @@ _raqm_u8_to_u32 (const char *text, size_t len, uint32_t *unicode)
return (out_utf32 - unicode);
}
static void *
_raqm_get_utf16_codepoint (const void *str,
uint32_t *out_codepoint)
{
const uint16_t *s = (const uint16_t *)str;
if (s[0] > 0xD800 && s[0] < 0xDBFF)
{
if (s[1] > 0xDC00 && s[1] < 0xDFFF)
{
uint32_t X = ((s[0] & ((1 << 6) -1)) << 10) | (s[1] & ((1 << 10) -1));
uint32_t W = (s[0] >> 6) & ((1 << 5) - 1);
*out_codepoint = (W+1) << 16 | X;
s += 2;
}
else
{
/* A single high surrogate, this is an error. */
*out_codepoint = s[0];
s += 1;
}
}
else
{
*out_codepoint = s[0];
s += 1;
}
return (void *)s;
}
static size_t
_raqm_u16_to_u32 (const uint16_t *text, size_t len, uint32_t *unicode)
{
size_t in_len = 0;
uint32_t *out_utf32 = unicode;
const uint16_t *in_utf16 = text;
while ((*in_utf16 != '\0') && (in_len < len))
{
in_utf16 = _raqm_get_utf16_codepoint (in_utf16, out_utf32);
++out_utf32;
++in_len;
}
return (out_utf32 - unicode);
}
/**
* raqm_set_text_utf8:
* @rq: a #raqm_t.
@ -584,7 +649,7 @@ _raqm_u8_to_u32 (const char *text, size_t len, uint32_t *unicode)
* Same as raqm_set_text(), but for text encoded in UTF-8 encoding.
*
* Return value:
* %true if no errors happened, %false otherwise.
* `true` if no errors happened, `false` otherwise.
*
* Since: 0.1
*/
@ -604,7 +669,7 @@ raqm_set_text_utf8 (raqm_t *rq,
if (!len)
return true;
if (!_raqm_alloc_text(rq, len, true))
if (!_raqm_alloc_text(rq, len, true, false))
return false;
rq->text_len = _raqm_u8_to_u32 (text, len, rq->text);
@ -614,6 +679,44 @@ raqm_set_text_utf8 (raqm_t *rq,
return true;
}
/**
* raqm_set_text_utf16:
* @rq: a #raqm_t.
* @text: a UTF-16 encoded text string.
* @len: the length of @text in UTF-16 shorts.
*
* Same as raqm_set_text(), but for text encoded in UTF-16 encoding.
*
* Return value:
* `true` if no errors happened, `false` otherwise.
*
* Since: 0.10
*/
bool
raqm_set_text_utf16 (raqm_t *rq,
const uint16_t *text,
size_t len)
{
if (!rq || !text)
return false;
/* Call raqm_clear_contents to reuse this raqm_t */
if (rq->text_len)
return false;
/* Empty string, dont fail but do nothing */
if (!len)
return true;
if (!_raqm_alloc_text(rq, len, false, true))
return false;
rq->text_len = _raqm_u16_to_u32 (text, len, rq->text);
memcpy (rq->text_utf16, text, sizeof (uint16_t) * len);
_raqm_init_text_info (rq);
return true;
}
/**
* raqm_set_par_direction:
* @rq: a #raqm_t.
@ -640,7 +743,7 @@ raqm_set_text_utf8 (raqm_t *rq,
* text.
*
* Return value:
* %true if no errors happened, %false otherwise.
* `true` if no errors happened, `false` otherwise.
*
* Since: 0.1
*/
@ -673,7 +776,7 @@ raqm_set_par_direction (raqm_t *rq,
* parts of the text.
*
* Return value:
* %true if no errors happened, %false otherwise.
* `true` if no errors happened, `false` otherwise.
*
* Stability:
* Unstable
@ -687,7 +790,7 @@ raqm_set_language (raqm_t *rq,
size_t len)
{
hb_language_t language;
size_t end = start + len;
size_t end;
if (!rq)
return false;
@ -695,11 +798,8 @@ raqm_set_language (raqm_t *rq,
if (!rq->text_len)
return true;
if (rq->text_utf8)
{
start = _raqm_u8_to_u32_index (rq, start);
end = _raqm_u8_to_u32_index (rq, end);
}
end = _raqm_encoding_to_u32_index (rq, start + len);
start = _raqm_encoding_to_u32_index (rq, start);
if (start >= rq->text_len || end > rq->text_len)
return false;
@ -716,11 +816,37 @@ raqm_set_language (raqm_t *rq,
return true;
}
static bool
_raqm_add_font_feature (raqm_t *rq,
hb_feature_t fea)
{
void* new_features;
if (!rq)
return false;
new_features = realloc (rq->features,
sizeof (hb_feature_t) * (rq->features_len + 1));
if (!new_features)
return false;
if (fea.start != HB_FEATURE_GLOBAL_START)
fea.start = _raqm_encoding_to_u32_index (rq, fea.start);
if (fea.end != HB_FEATURE_GLOBAL_END)
fea.end = _raqm_encoding_to_u32_index (rq, fea.end);
rq->features = new_features;
rq->features[rq->features_len] = fea;
rq->features_len++;
return true;
}
/**
* raqm_add_font_feature:
* @rq: a #raqm_t.
* @feature: (transfer none): a font feature string.
* @len: length of @feature, -1 for %NULL-terminated.
* @len: length of @feature, -1 for `NULL`-terminated.
*
* Adds a font feature to be used by the #raqm_t during text layout. This is
* usually used to turn on optional font features that are not enabled by
@ -734,7 +860,7 @@ raqm_set_language (raqm_t *rq,
* end of the features list and can potentially override previous features.
*
* Return value:
* %true if parsing @feature succeeded, %false otherwise.
* `true` if parsing @feature succeeded, `false` otherwise.
*
* Since: 0.1
*/
@ -751,16 +877,7 @@ raqm_add_font_feature (raqm_t *rq,
ok = hb_feature_from_string (feature, len, &fea);
if (ok)
{
void* new_features = realloc (rq->features,
sizeof (hb_feature_t) * (rq->features_len + 1));
if (!new_features)
return false;
rq->features = new_features;
rq->features[rq->features_len] = fea;
rq->features_len++;
}
_raqm_add_font_feature (rq, fea);
return ok;
}
@ -817,7 +934,7 @@ _raqm_set_freetype_face (raqm_t *rq,
* See also raqm_set_freetype_face_range().
*
* Return value:
* %true if no errors happened, %false otherwise.
* `true` if no errors happened, `false` otherwise.
*
* Since: 0.1
*/
@ -832,21 +949,23 @@ raqm_set_freetype_face (raqm_t *rq,
* raqm_set_freetype_face_range:
* @rq: a #raqm_t.
* @face: an #FT_Face.
* @start: index of first character that should use @face.
* @len: number of characters using @face.
* @start: index of first character that should use @face from the input string.
* @len: number of elements using @face.
*
* Sets an #FT_Face to be used for @len-number of characters staring at @start.
* The @start and @len are input string array indices (i.e. counting bytes in
* UTF-8 and scaler values in UTF-32).
* The @start and @len are input string array indices, counting elements
* according to the underlying encoding. @start must always be aligned to the
* start of an encoded codepoint, and @len must always end at a codepoint's
* final element.
*
* This method can be used repeatedly to set different faces for different
* parts of the text. It is the responsibility of the client to make sure that
* face ranges cover the whole text.
* face ranges cover the whole text, and is properly aligned.
*
* See also raqm_set_freetype_face().
*
* Return value:
* %true if no errors happened, %false otherwise.
* `true` if no errors happened, `false` otherwise.
*
* Since: 0.1
*/
@ -856,7 +975,7 @@ raqm_set_freetype_face_range (raqm_t *rq,
size_t start,
size_t len)
{
size_t end = start + len;
size_t end;
if (!rq)
return false;
@ -864,11 +983,8 @@ raqm_set_freetype_face_range (raqm_t *rq,
if (!rq->text_len)
return true;
if (rq->text_utf8)
{
start = _raqm_u8_to_u32_index (rq, start);
end = _raqm_u8_to_u32_index (rq, end);
}
end = _raqm_encoding_to_u32_index (rq, start + len);
start = _raqm_encoding_to_u32_index (rq, start);
return _raqm_set_freetype_face (rq, face, start, end);
}
@ -909,7 +1025,7 @@ _raqm_set_freetype_load_flags (raqm_t *rq,
* older version the flags will be ignored.
*
* Return value:
* %true if no errors happened, %false otherwise.
* `true` if no errors happened, `false` otherwise.
*
* Since: 0.3
*/
@ -943,7 +1059,7 @@ raqm_set_freetype_load_flags (raqm_t *rq,
* See also raqm_set_freetype_load_flags().
*
* Return value:
* %true if no errors happened, %false otherwise.
* `true` if no errors happened, `false` otherwise.
*
* Since: 0.9
*/
@ -953,7 +1069,7 @@ raqm_set_freetype_load_flags_range (raqm_t *rq,
size_t start,
size_t len)
{
size_t end = start + len;
size_t end;
if (!rq)
return false;
@ -961,15 +1077,161 @@ raqm_set_freetype_load_flags_range (raqm_t *rq,
if (!rq->text_len)
return true;
if (rq->text_utf8)
{
start = _raqm_u8_to_u32_index (rq, start);
end = _raqm_u8_to_u32_index (rq, end);
}
end = _raqm_encoding_to_u32_index (rq, start + len);
start = _raqm_encoding_to_u32_index (rq, start);
return _raqm_set_freetype_load_flags (rq, flags, start, end);
}
static bool
_raqm_set_spacing (raqm_t *rq,
int spacing,
bool word_spacing,
size_t start,
size_t end)
{
if (!rq)
return false;
if (!rq->text_len)
return true;
if (start >= rq->text_len || end > rq->text_len)
return false;
if (!rq->text_info)
return false;
for (size_t i = start; i < end; i++)
{
bool set_spacing = i == 0;
if (!set_spacing)
set_spacing = _raqm_allowed_grapheme_boundary (rq->text[i-1], rq->text[i]);
if (set_spacing)
{
if (word_spacing)
{
if (_raqm_allowed_grapheme_boundary (rq->text[i], rq->text[i+1]))
{
/* CSS word seperators, word spacing is only applied on these.*/
if (rq->text[i] == 0x0020 || /* Space */
rq->text[i] == 0x00A0 || /* No Break Space */
rq->text[i] == 0x1361 || /* Ethiopic Word Space */
rq->text[i] == 0x10100 || /* Aegean Word Seperator Line */
rq->text[i] == 0x10101 || /* Aegean Word Seperator Dot */
rq->text[i] == 0x1039F || /* Ugaric Word Divider */
rq->text[i] == 0x1091F) /* Phoenician Word Separator */
{
rq->text_info[i].spacing_after = spacing;
}
}
}
else
{
rq->text_info[i].spacing_after = spacing;
}
}
}
return true;
}
/**
* raqm_set_letter_spacing_range:
* @rq: a #raqm_t.
* @spacing: amount of spacing in Freetype Font Units (26.6 format).
* @start: index of first character that should use @spacing.
* @len: number of characters using @spacing.
*
* Set the letter spacing or tracking for a given range, the value
* will be added onto the advance and offset for RTL, and the advance for
* other directions. Letter spacing will be applied between characters, so
* the last character will not have spacing applied after it.
* Note that not all scripts have a letter-spacing tradition,
* for example, Arabic does not, while Devanagari does.
*
* This will also add disable `liga`, `clig`, `hlig`, `dlig`, and `calt` font
* features to the internal features list, so call this function after setting
* the font features for best spacing results.
*
* Return value:
* `true` if no errors happened, `false` otherwise.
*
* Since: 0.10
*/
bool
raqm_set_letter_spacing_range(raqm_t *rq,
int spacing,
size_t start,
size_t len)
{
size_t end;
if (!rq)
return false;
if (!rq->text_len)
return true;
end = start + len - 1;
if (spacing != 0)
{
#define NUM_TAGS 5
static char *tags[NUM_TAGS] = { "clig", "liga", "hlig", "dlig", "calt" };
for (size_t i = 0; i < NUM_TAGS; i++)
{
hb_feature_t fea = { hb_tag_from_string(tags[i], 5), 0, start, end };
_raqm_add_font_feature (rq, fea);
}
#undef NUM_TAGS
}
start = _raqm_encoding_to_u32_index (rq, start);
end = _raqm_encoding_to_u32_index (rq, end);
return _raqm_set_spacing (rq, spacing, false, start, end);
}
/**
* raqm_set_word_spacing_range:
* @rq: a #raqm_t.
* @spacing: amount of spacing in Freetype Font Units (26.6 format).
* @start: index of first character that should use @spacing.
* @len: number of characters using @spacing.
*
* Set the word spacing for a given range. Word spacing will only be applied to
* 'word separator' characters, such as 'space', 'no break space' and
* Ethiopic word separator'.
* The value will be added onto the advance and offset for RTL, and the advance
* for other directions.
*
* Return value:
* `true` if no errors happened, `false` otherwise.
*
* Since: 0.10
*/
bool
raqm_set_word_spacing_range(raqm_t *rq,
int spacing,
size_t start,
size_t len)
{
size_t end;
if (!rq)
return false;
if (!rq->text_len)
return true;
end = _raqm_encoding_to_u32_index (rq, start + len);
start = _raqm_encoding_to_u32_index (rq, start);
return _raqm_set_spacing (rq, spacing, true, start, end);
}
/**
* raqm_set_invisible_glyph:
* @rq: a #raqm_t.
@ -984,7 +1246,7 @@ raqm_set_freetype_load_flags_range (raqm_t *rq,
* If @gid is a positive number, it will be used for invisible glyphs.
*
* Return value:
* %true if no errors happened, %false otherwise.
* `true` if no errors happened, `false` otherwise.
*
* Since: 0.6
*/
@ -1014,7 +1276,7 @@ _raqm_shape (raqm_t *rq);
* text shaping, and any other part of the layout process.
*
* Return value:
* %true if the layout process was successful, %false otherwise.
* `true` if the layout process was successful, `false` otherwise.
*
* Since: 0.1
*/
@ -1048,7 +1310,9 @@ raqm_layout (raqm_t *rq)
static uint32_t
_raqm_u32_to_u8_index (raqm_t *rq,
uint32_t index);
static uint32_t
_raqm_u32_to_u16_index (raqm_t *rq,
uint32_t index);
/**
* raqm_get_glyphs:
* @rq: a #raqm_t.
@ -1059,7 +1323,7 @@ _raqm_u32_to_u8_index (raqm_t *rq,
* information.
*
* Return value: (transfer none):
* An array of #raqm_glyph_t, or %NULL in case of error. This is owned by @rq
* An array of #raqm_glyph_t, or `NULL` in case of error. This is owned by @rq
* and must not be freed.
*
* Since: 0.1
@ -1147,6 +1411,12 @@ raqm_get_glyphs (raqm_t *rq,
RAQM_TEST ("\n");
#endif
}
else if (rq->text_utf16)
{
for (size_t i = 0; i < count; i++)
rq->glyphs[i].cluster = _raqm_u32_to_u16_index (rq,
rq->glyphs[i].cluster);
}
return rq->glyphs;
}
@ -1194,8 +1464,10 @@ raqm_get_direction_at_index (raqm_t *rq,
for (raqm_run_t *run = rq->runs; run != NULL; run = run->next)
{
if (run->pos <= index && index < run->pos + run->len) {
switch (run->direction) {
if (run->pos <= index && index < run->pos + run->len)
{
switch (run->direction)
{
case HB_DIRECTION_LTR:
return RAQM_DIRECTION_LTR;
case HB_DIRECTION_RTL:
@ -1227,7 +1499,8 @@ _raqm_hb_dir (raqm_t *rq, _raqm_bidi_level_t level)
return dir;
}
typedef struct {
typedef struct
{
size_t pos;
size_t len;
_raqm_bidi_level_t level;
@ -1264,10 +1537,10 @@ _raqm_bidi_itemize (raqm_t *rq, size_t *run_count)
line = SBParagraphCreateLine (par, 0, par_len);
*run_count = SBLineGetRunCount (line);
if (SBParagraphGetBaseLevel (par) == 0)
rq->resolved_dir = RAQM_DIRECTION_LTR;
else
if (SBParagraphGetBaseLevel (par) == 1)
rq->resolved_dir = RAQM_DIRECTION_RTL;
else
rq->resolved_dir = RAQM_DIRECTION_LTR;
runs = malloc (sizeof (_raqm_bidi_run) * (*run_count));
if (runs)
@ -1418,10 +1691,10 @@ _raqm_bidi_itemize (raqm_t *rq, size_t *run_count)
rq->text_len, &par_type,
levels);
if (par_type == FRIBIDI_PAR_LTR)
rq->resolved_dir = RAQM_DIRECTION_LTR;
else
if (par_type == FRIBIDI_PAR_RTL)
rq->resolved_dir = RAQM_DIRECTION_RTL;
else
rq->resolved_dir = RAQM_DIRECTION_LTR;
if (max_level == 0)
goto done;
@ -1447,22 +1720,15 @@ _raqm_itemize (raqm_t *rq)
bool ok = true;
#ifdef RAQM_TESTING
switch (rq->base_dir)
{
case RAQM_DIRECTION_RTL:
RAQM_TEST ("Direction is: RTL\n\n");
break;
case RAQM_DIRECTION_LTR:
RAQM_TEST ("Direction is: LTR\n\n");
break;
case RAQM_DIRECTION_TTB:
RAQM_TEST ("Direction is: TTB\n\n");
break;
case RAQM_DIRECTION_DEFAULT:
default:
RAQM_TEST ("Direction is: DEFAULT\n\n");
break;
}
static char *dir_names[] = {
"DEFAULT",
"RTL",
"LTR",
"TTB"
};
assert (rq->base_dir < sizeof (dir_names));
RAQM_TEST ("Direction is: %s\n\n", dir_names[rq->base_dir]);
#endif
if (!_raqm_resolve_scripts (rq))
@ -1483,9 +1749,9 @@ _raqm_itemize (raqm_t *rq)
runs->len = rq->text_len;
runs->level = 0;
}
} else {
runs = _raqm_bidi_itemize (rq, &run_count);
}
else
runs = _raqm_bidi_itemize (rq, &run_count);
if (!runs)
{
@ -1494,6 +1760,9 @@ _raqm_itemize (raqm_t *rq)
}
#ifdef RAQM_TESTING
assert (rq->resolved_dir < sizeof (dir_names));
if (rq->base_dir == RAQM_DIRECTION_DEFAULT)
RAQM_TEST ("Resolved direction is: %s\n\n", dir_names[rq->resolved_dir]);
RAQM_TEST ("Number of runs before script itemization: %zu\n\n", run_count);
RAQM_TEST ("BiDi Runs:\n");
@ -1617,7 +1886,8 @@ done:
}
/* Stack to handle script detection */
typedef struct {
typedef struct
{
size_t capacity;
size_t size;
int *pair_index;
@ -1910,15 +2180,47 @@ _raqm_shape (raqm_t *rq)
{
FT_Matrix matrix;
hb_glyph_info_t *info;
hb_glyph_position_t *pos;
unsigned int len;
FT_Get_Transform (hb_ft_font_get_face (run->font), &matrix, NULL);
pos = hb_buffer_get_glyph_positions (run->buffer, &len);
info = hb_buffer_get_glyph_infos (run->buffer, &len);
for (unsigned int i = 0; i < len; i++)
{
_raqm_ft_transform (&pos[i].x_advance, &pos[i].y_advance, matrix);
_raqm_ft_transform (&pos[i].x_offset, &pos[i].y_offset, matrix);
bool set_spacing = false;
if (run->direction == HB_DIRECTION_RTL)
{
set_spacing = i == 0;
if (!set_spacing)
set_spacing = info[i].cluster != info[i-1].cluster;
}
else
{
set_spacing = i == len - 1;
if (!set_spacing)
set_spacing = info[i].cluster != info[i+1].cluster;
}
_raqm_text_info rq_info = rq->text_info[info[i].cluster];
if (rq_info.spacing_after != 0 && set_spacing)
{
if (run->direction == HB_DIRECTION_TTB)
pos[i].y_advance -= rq_info.spacing_after;
else if (run->direction == HB_DIRECTION_RTL)
{
pos[i].x_advance += rq_info.spacing_after;
pos[i].x_offset += rq_info.spacing_after;
}
else
pos[i].x_advance += rq_info.spacing_after;
}
}
}
}
@ -1954,9 +2256,9 @@ _raqm_u32_to_u8_index (raqm_t *rq,
}
/* Convert index from UTF-8 to UTF-32 */
static uint32_t
static size_t
_raqm_u8_to_u32_index (raqm_t *rq,
uint32_t index)
size_t index)
{
const unsigned char *s = (const unsigned char *) rq->text_utf8;
const unsigned char *t = s;
@ -1982,9 +2284,64 @@ _raqm_u8_to_u32_index (raqm_t *rq,
return length;
}
static bool
_raqm_allowed_grapheme_boundary (hb_codepoint_t l_char,
hb_codepoint_t r_char);
/* Count equivalent UTF-16 short in codepoint */
static size_t
_raqm_count_codepoint_utf16_short (uint32_t chr)
{
if (chr > 0x010000)
return 2;
else
return 1;
}
/* Convert index from UTF-32 to UTF-16 */
static uint32_t
_raqm_u32_to_u16_index (raqm_t *rq,
uint32_t index)
{
size_t length = 0;
for (uint32_t i = 0; i < index; ++i)
length += _raqm_count_codepoint_utf16_short (rq->text[i]);
return length;
}
/* Convert index from UTF-16 to UTF-32 */
static size_t
_raqm_u16_to_u32_index (raqm_t *rq,
size_t index)
{
const uint16_t *s = (const uint16_t *) rq->text_utf16;
const uint16_t *t = s;
size_t length = 0;
while (((size_t) (s - t) < index) && ('\0' != *s))
{
if (*s < 0xD800 || *s > 0xDBFF)
s += 1;
else
s += 2;
length++;
}
if ((size_t) (s-t) > index)
length--;
return length;
}
static inline size_t
_raqm_encoding_to_u32_index (raqm_t *rq,
size_t index)
{
if (rq->text_utf8)
return _raqm_u8_to_u32_index (rq, index);
else if (rq->text_utf16)
return _raqm_u16_to_u32_index (rq, index);
return index;
}
static bool
_raqm_in_hangul_syllable (hb_codepoint_t ch);
@ -2001,7 +2358,7 @@ _raqm_in_hangul_syllable (hb_codepoint_t ch);
* character is left-to-right, then the cursor will be at the right of it.
*
* Return value:
* %true if the process was successful, %false otherwise.
* `true` if the process was successful, `false` otherwise.
*
* Since: 0.2
*/
@ -2018,8 +2375,7 @@ raqm_index_to_position (raqm_t *rq,
if (rq == NULL)
return false;
if (rq->text_utf8)
*index = _raqm_u8_to_u32_index (rq, *index);
*index = _raqm_encoding_to_u32_index (rq, *index);
if (*index >= rq->text_len)
return false;
@ -2077,6 +2433,8 @@ raqm_index_to_position (raqm_t *rq,
found:
if (rq->text_utf8)
*index = _raqm_u32_to_u8_index (rq, *index);
else if (rq->text_utf16)
*index = _raqm_u32_to_u16_index (rq, *index);
RAQM_TEST ("The position is %d at index %zu\n",*x ,*index);
return true;
}
@ -2093,7 +2451,7 @@ found:
* @index.
*
* Return value:
* %true if the process was successful, %false in case of error.
* `true` if the process was successful, `false` in case of error.
*
* Since: 0.2
*/
@ -2371,8 +2729,8 @@ raqm_version_string (void)
* Checks if library version is less than or equal the specified version.
*
* Return value:
* %true if library version is less than or equal the specfied version, %false
* otherwise.
* `true` if library version is less than or equal the specified version,
* `false` otherwise.
*
* Since: 0.7
**/
@ -2393,8 +2751,8 @@ raqm_version_atleast (unsigned int major,
* Checks if library version is less than or equal the specified version.
*
* Return value:
* %true if library version is less than or equal the specfied version, %false
* otherwise.
* `true` if library version is less than or equal the specified version,
* `false` otherwise.
*
* Since: 0.7
**/

View File

@ -118,6 +118,10 @@ RAQM_API bool
raqm_set_text_utf8 (raqm_t *rq,
const char *text,
size_t len);
RAQM_API bool
raqm_set_text_utf16 (raqm_t *rq,
const uint16_t *text,
size_t len);
RAQM_API bool
raqm_set_par_direction (raqm_t *rq,
@ -154,6 +158,17 @@ raqm_set_freetype_load_flags_range (raqm_t *rq,
size_t start,
size_t len);
RAQM_API bool
raqm_set_letter_spacing_range(raqm_t *rq,
int spacing,
size_t start,
size_t len);
RAQM_API bool
raqm_set_word_spacing_range(raqm_t *rq,
int spacing,
size_t start,
size_t len);
RAQM_API bool
raqm_set_invisible_glyph (raqm_t *rq,
int gid);

View File

@ -152,9 +152,9 @@ deps = {
"libs": [r"*.lib"],
},
"xz": {
"url": SF_PROJECTS + "/lzmautils/files/xz-5.4.0.tar.gz/download",
"filename": "xz-5.4.0.tar.gz",
"dir": "xz-5.4.0",
"url": SF_PROJECTS + "/lzmautils/files/xz-5.4.1.tar.gz/download",
"filename": "xz-5.4.1.tar.gz",
"dir": "xz-5.4.1",
"license": "COPYING",
"patch": {
r"src\liblzma\api\lzma.h": {