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

@ -49,7 +49,6 @@ jobs:
${{ matrix.package }}-python3-numpy \ ${{ matrix.package }}-python3-numpy \
${{ matrix.package }}-python3-olefile \ ${{ matrix.package }}-python3-olefile \
${{ matrix.package }}-python3-pip \ ${{ matrix.package }}-python3-pip \
${{ matrix.package }}-python-pyqt6 \
${{ matrix.package }}-python3-setuptools \ ${{ matrix.package }}-python3-setuptools \
${{ matrix.package }}-freetype \ ${{ matrix.package }}-freetype \
${{ matrix.package }}-gcc \ ${{ matrix.package }}-gcc \
@ -63,6 +62,11 @@ jobs:
${{ matrix.package }}-openjpeg2 \ ${{ matrix.package }}-openjpeg2 \
subversion 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 python3 -m pip install pyroma pytest pytest-cov pytest-timeout
pushd depends && ./install_extra_test_images.sh && popd pushd depends && ./install_extra_test_images.sh && popd

View File

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

View File

@ -24,6 +24,7 @@ from .helper import assert_image_equal, hopper
ImageFilter.ModeFilter, ImageFilter.ModeFilter,
ImageFilter.GaussianBlur, ImageFilter.GaussianBlur,
ImageFilter.GaussianBlur(5), ImageFilter.GaussianBlur(5),
ImageFilter.BoxBlur(0),
ImageFilter.BoxBlur(5), ImageFilter.BoxBlur(5),
ImageFilter.UnsharpMask, ImageFilter.UnsharpMask,
ImageFilter.UnsharpMask(10), ImageFilter.UnsharpMask(10),
@ -173,3 +174,14 @@ def test_consistency_5x5(mode):
Image.merge(mode, source[: len(mode)]).filter(kernel), Image.merge(mode, source[: len(mode)]).filter(kernel),
Image.merge(mode, reference[: len(mode)]), 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 # 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 ./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 :target: https://fosstodon.org/@pillow
:alt: Follow on https://fosstodon.org/@pillow :alt: Follow on https://fosstodon.org/@pillow
.. raw:: html
<link rel="me" href="https://fosstodon.org/@pillow">
Overview 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 | | Operating system | | Tested Python | | Latest tested | | Tested |
| | | versions | | Pillow version | | processors | | | | 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 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 | | 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 | | | | 3.6 | 8.4.0 | |
+----------------------------------+---------------------------+------------------+--------------+ +----------------------------------+---------------------------+------------------+--------------+

View File

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

View File

@ -237,6 +237,9 @@ ImagingBoxBlur(Imaging imOut, Imaging imIn, float radius, int n) {
if (n < 1) { if (n < 1) {
return ImagingError_ValueError("number of passes must be greater than zero"); 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 || if (strcmp(imIn->mode, imOut->mode) || imIn->type != imOut->type ||
imIn->bands != imOut->bands || imIn->xsize != imOut->xsize || 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. As a result, Raqm can support most writing systems covered by Unicode.
The documentation can be accessed on the web at: 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 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”. 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 [1]: https://github.com/fribidi/fribidi
[2]: https://github.com/Tehreer/SheenBidi [2]: https://github.com/Tehreer/SheenBidi
[3]: https://github.com/harfbuzz/harfbuzz [3]: https://github.com/harfbuzz/harfbuzz
[4]: https://freetype.org/ [4]: https://www.freetype.org
[5]: https://www.gtk.org/gtk-doc [5]: https://www.gtk.org/gtk-doc

View File

@ -32,10 +32,10 @@
#define _RAQM_VERSION_H_ #define _RAQM_VERSION_H_
#define RAQM_VERSION_MAJOR 0 #define RAQM_VERSION_MAJOR 0
#define RAQM_VERSION_MINOR 9 #define RAQM_VERSION_MINOR 10
#define RAQM_VERSION_MICRO 0 #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) \ #define RAQM_VERSION_ATLEAST(major,minor,micro) \
((major)*10000+(minor)*100+(micro) <= \ ((major)*10000+(minor)*100+(micro) <= \

View File

@ -171,19 +171,23 @@
typedef FriBidiLevel _raqm_bidi_level_t; typedef FriBidiLevel _raqm_bidi_level_t;
#endif #endif
typedef struct { typedef struct
{
FT_Face ftface; FT_Face ftface;
int ftloadflags; int ftloadflags;
hb_language_t lang; hb_language_t lang;
hb_script_t script; hb_script_t script;
int spacing_after;
} _raqm_text_info; } _raqm_text_info;
typedef struct _raqm_run raqm_run_t; typedef struct _raqm_run raqm_run_t;
struct _raqm { struct _raqm
{
int ref_count; int ref_count;
uint32_t *text; uint32_t *text;
uint16_t *text_utf16;
char *text_utf8; char *text_utf8;
size_t text_len; size_t text_len;
size_t text_capacity_bytes; size_t text_capacity_bytes;
@ -205,7 +209,8 @@ struct _raqm {
int invisible_glyph; int invisible_glyph;
}; };
struct _raqm_run { struct _raqm_run
{
uint32_t pos; uint32_t pos;
uint32_t len; uint32_t len;
@ -217,9 +222,13 @@ struct _raqm_run {
raqm_run_t *next; raqm_run_t *next;
}; };
static uint32_t static size_t
_raqm_u8_to_u32_index (raqm_t *rq, _raqm_encoding_to_u32_index (raqm_t *rq,
uint32_t index); size_t index);
static bool
_raqm_allowed_grapheme_boundary (hb_codepoint_t l_char,
hb_codepoint_t r_char);
static void static void
_raqm_init_text_info (raqm_t *rq) _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].ftloadflags = -1;
rq->text_info[i].lang = default_lang; rq->text_info[i].lang = default_lang;
rq->text_info[i].script = HB_SCRIPT_INVALID; 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) if (a.script != b.script)
return false; return false;
/* Spacing shouldn't break runs, so we don't compare them here. */
return true; return true;
} }
@ -273,6 +285,7 @@ _raqm_free_text(raqm_t* rq)
rq->text = NULL; rq->text = NULL;
rq->text_info = NULL; rq->text_info = NULL;
rq->text_utf8 = NULL; rq->text_utf8 = NULL;
rq->text_utf16 = NULL;
rq->text_len = 0; rq->text_len = 0;
rq->text_capacity_bytes = 0; rq->text_capacity_bytes = 0;
} }
@ -280,12 +293,15 @@ _raqm_free_text(raqm_t* rq)
static bool static bool
_raqm_alloc_text(raqm_t *rq, _raqm_alloc_text(raqm_t *rq,
size_t len, size_t len,
bool need_utf8) bool need_utf8,
bool need_utf16)
{ {
/* Allocate contiguous memory block for texts and text_info */ /* Allocate contiguous memory block for texts and text_info */
size_t mem_size = (sizeof (uint32_t) + sizeof (_raqm_text_info)) * len; size_t mem_size = (sizeof (uint32_t) + sizeof (_raqm_text_info)) * len;
if (need_utf8) if (need_utf8)
mem_size += sizeof (char) * len; mem_size += sizeof (char) * len;
else if (need_utf16)
mem_size += sizeof (uint16_t) * len;
if (mem_size > rq->text_capacity_bytes) 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_info = (_raqm_text_info*)(rq->text + len);
rq->text_utf8 = need_utf8 ? (char*)(rq->text_info + len) : NULL; 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; return true;
} }
@ -357,7 +374,7 @@ _raqm_free_runs (raqm_run_t *runs)
* Return value: * Return value:
* A newly allocated #raqm_t with a reference count of 1. The initial reference * 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 * 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 * Since: 0.1
*/ */
@ -381,6 +398,7 @@ raqm_create (void)
rq->invisible_glyph = 0; rq->invisible_glyph = 0;
rq->text = NULL; rq->text = NULL;
rq->text_utf16 = NULL;
rq->text_utf8 = NULL; rq->text_utf8 = NULL;
rq->text_info = NULL; rq->text_info = NULL;
rq->text_capacity_bytes = 0; rq->text_capacity_bytes = 0;
@ -498,7 +516,7 @@ raqm_clear_contents (raqm_t *rq)
* separately can give improper output. * separately can give improper output.
* *
* Return value: * Return value:
* %true if no errors happened, %false otherwise. * `true` if no errors happened, `false` otherwise.
* *
* Since: 0.1 * Since: 0.1
*/ */
@ -518,7 +536,7 @@ raqm_set_text (raqm_t *rq,
if (!len) if (!len)
return true; return true;
if (!_raqm_alloc_text(rq, len, false)) if (!_raqm_alloc_text(rq, len, false, false))
return false; return false;
rq->text_len = len; 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); 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: * raqm_set_text_utf8:
* @rq: a #raqm_t. * @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. * Same as raqm_set_text(), but for text encoded in UTF-8 encoding.
* *
* Return value: * Return value:
* %true if no errors happened, %false otherwise. * `true` if no errors happened, `false` otherwise.
* *
* Since: 0.1 * Since: 0.1
*/ */
@ -604,7 +669,7 @@ raqm_set_text_utf8 (raqm_t *rq,
if (!len) if (!len)
return true; return true;
if (!_raqm_alloc_text(rq, len, true)) if (!_raqm_alloc_text(rq, len, true, false))
return false; return false;
rq->text_len = _raqm_u8_to_u32 (text, len, rq->text); rq->text_len = _raqm_u8_to_u32 (text, len, rq->text);
@ -614,6 +679,44 @@ raqm_set_text_utf8 (raqm_t *rq,
return true; 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: * raqm_set_par_direction:
* @rq: a #raqm_t. * @rq: a #raqm_t.
@ -640,7 +743,7 @@ raqm_set_text_utf8 (raqm_t *rq,
* text. * text.
* *
* Return value: * Return value:
* %true if no errors happened, %false otherwise. * `true` if no errors happened, `false` otherwise.
* *
* Since: 0.1 * Since: 0.1
*/ */
@ -673,7 +776,7 @@ raqm_set_par_direction (raqm_t *rq,
* parts of the text. * parts of the text.
* *
* Return value: * Return value:
* %true if no errors happened, %false otherwise. * `true` if no errors happened, `false` otherwise.
* *
* Stability: * Stability:
* Unstable * Unstable
@ -687,7 +790,7 @@ raqm_set_language (raqm_t *rq,
size_t len) size_t len)
{ {
hb_language_t language; hb_language_t language;
size_t end = start + len; size_t end;
if (!rq) if (!rq)
return false; return false;
@ -695,11 +798,8 @@ raqm_set_language (raqm_t *rq,
if (!rq->text_len) if (!rq->text_len)
return true; return true;
if (rq->text_utf8) end = _raqm_encoding_to_u32_index (rq, start + len);
{ start = _raqm_encoding_to_u32_index (rq, start);
start = _raqm_u8_to_u32_index (rq, start);
end = _raqm_u8_to_u32_index (rq, end);
}
if (start >= rq->text_len || end > rq->text_len) if (start >= rq->text_len || end > rq->text_len)
return false; return false;
@ -716,11 +816,37 @@ raqm_set_language (raqm_t *rq,
return true; 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: * raqm_add_font_feature:
* @rq: a #raqm_t. * @rq: a #raqm_t.
* @feature: (transfer none): a font feature string. * @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 * 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 * 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. * end of the features list and can potentially override previous features.
* *
* Return value: * Return value:
* %true if parsing @feature succeeded, %false otherwise. * `true` if parsing @feature succeeded, `false` otherwise.
* *
* Since: 0.1 * Since: 0.1
*/ */
@ -751,16 +877,7 @@ raqm_add_font_feature (raqm_t *rq,
ok = hb_feature_from_string (feature, len, &fea); ok = hb_feature_from_string (feature, len, &fea);
if (ok) if (ok)
{ _raqm_add_font_feature (rq, fea);
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++;
}
return ok; return ok;
} }
@ -817,7 +934,7 @@ _raqm_set_freetype_face (raqm_t *rq,
* See also raqm_set_freetype_face_range(). * See also raqm_set_freetype_face_range().
* *
* Return value: * Return value:
* %true if no errors happened, %false otherwise. * `true` if no errors happened, `false` otherwise.
* *
* Since: 0.1 * Since: 0.1
*/ */
@ -832,21 +949,23 @@ raqm_set_freetype_face (raqm_t *rq,
* raqm_set_freetype_face_range: * raqm_set_freetype_face_range:
* @rq: a #raqm_t. * @rq: a #raqm_t.
* @face: an #FT_Face. * @face: an #FT_Face.
* @start: index of first character that should use @face. * @start: index of first character that should use @face from the input string.
* @len: number of characters using @face. * @len: number of elements using @face.
* *
* Sets an #FT_Face to be used for @len-number of characters staring at @start. * 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 * The @start and @len are input string array indices, counting elements
* UTF-8 and scaler values in UTF-32). * 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 * 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 * 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(). * See also raqm_set_freetype_face().
* *
* Return value: * Return value:
* %true if no errors happened, %false otherwise. * `true` if no errors happened, `false` otherwise.
* *
* Since: 0.1 * Since: 0.1
*/ */
@ -856,7 +975,7 @@ raqm_set_freetype_face_range (raqm_t *rq,
size_t start, size_t start,
size_t len) size_t len)
{ {
size_t end = start + len; size_t end;
if (!rq) if (!rq)
return false; return false;
@ -864,11 +983,8 @@ raqm_set_freetype_face_range (raqm_t *rq,
if (!rq->text_len) if (!rq->text_len)
return true; return true;
if (rq->text_utf8) end = _raqm_encoding_to_u32_index (rq, start + len);
{ start = _raqm_encoding_to_u32_index (rq, start);
start = _raqm_u8_to_u32_index (rq, start);
end = _raqm_u8_to_u32_index (rq, end);
}
return _raqm_set_freetype_face (rq, face, start, end); 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. * older version the flags will be ignored.
* *
* Return value: * Return value:
* %true if no errors happened, %false otherwise. * `true` if no errors happened, `false` otherwise.
* *
* Since: 0.3 * Since: 0.3
*/ */
@ -943,7 +1059,7 @@ raqm_set_freetype_load_flags (raqm_t *rq,
* See also raqm_set_freetype_load_flags(). * See also raqm_set_freetype_load_flags().
* *
* Return value: * Return value:
* %true if no errors happened, %false otherwise. * `true` if no errors happened, `false` otherwise.
* *
* Since: 0.9 * Since: 0.9
*/ */
@ -953,7 +1069,7 @@ raqm_set_freetype_load_flags_range (raqm_t *rq,
size_t start, size_t start,
size_t len) size_t len)
{ {
size_t end = start + len; size_t end;
if (!rq) if (!rq)
return false; return false;
@ -961,15 +1077,161 @@ raqm_set_freetype_load_flags_range (raqm_t *rq,
if (!rq->text_len) if (!rq->text_len)
return true; return true;
if (rq->text_utf8) end = _raqm_encoding_to_u32_index (rq, start + len);
{ start = _raqm_encoding_to_u32_index (rq, start);
start = _raqm_u8_to_u32_index (rq, start);
end = _raqm_u8_to_u32_index (rq, end);
}
return _raqm_set_freetype_load_flags (rq, flags, start, end); 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: * raqm_set_invisible_glyph:
* @rq: a #raqm_t. * @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. * If @gid is a positive number, it will be used for invisible glyphs.
* *
* Return value: * Return value:
* %true if no errors happened, %false otherwise. * `true` if no errors happened, `false` otherwise.
* *
* Since: 0.6 * Since: 0.6
*/ */
@ -1014,7 +1276,7 @@ _raqm_shape (raqm_t *rq);
* text shaping, and any other part of the layout process. * text shaping, and any other part of the layout process.
* *
* Return value: * Return value:
* %true if the layout process was successful, %false otherwise. * `true` if the layout process was successful, `false` otherwise.
* *
* Since: 0.1 * Since: 0.1
*/ */
@ -1048,7 +1310,9 @@ raqm_layout (raqm_t *rq)
static uint32_t static uint32_t
_raqm_u32_to_u8_index (raqm_t *rq, _raqm_u32_to_u8_index (raqm_t *rq,
uint32_t index); uint32_t index);
static uint32_t
_raqm_u32_to_u16_index (raqm_t *rq,
uint32_t index);
/** /**
* raqm_get_glyphs: * raqm_get_glyphs:
* @rq: a #raqm_t. * @rq: a #raqm_t.
@ -1059,7 +1323,7 @@ _raqm_u32_to_u8_index (raqm_t *rq,
* information. * information.
* *
* Return value: (transfer none): * 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. * and must not be freed.
* *
* Since: 0.1 * Since: 0.1
@ -1147,6 +1411,12 @@ raqm_get_glyphs (raqm_t *rq,
RAQM_TEST ("\n"); RAQM_TEST ("\n");
#endif #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; 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) for (raqm_run_t *run = rq->runs; run != NULL; run = run->next)
{ {
if (run->pos <= index && index < run->pos + run->len) { if (run->pos <= index && index < run->pos + run->len)
switch (run->direction) { {
switch (run->direction)
{
case HB_DIRECTION_LTR: case HB_DIRECTION_LTR:
return RAQM_DIRECTION_LTR; return RAQM_DIRECTION_LTR;
case HB_DIRECTION_RTL: case HB_DIRECTION_RTL:
@ -1227,7 +1499,8 @@ _raqm_hb_dir (raqm_t *rq, _raqm_bidi_level_t level)
return dir; return dir;
} }
typedef struct { typedef struct
{
size_t pos; size_t pos;
size_t len; size_t len;
_raqm_bidi_level_t level; _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); line = SBParagraphCreateLine (par, 0, par_len);
*run_count = SBLineGetRunCount (line); *run_count = SBLineGetRunCount (line);
if (SBParagraphGetBaseLevel (par) == 0) if (SBParagraphGetBaseLevel (par) == 1)
rq->resolved_dir = RAQM_DIRECTION_LTR;
else
rq->resolved_dir = RAQM_DIRECTION_RTL; rq->resolved_dir = RAQM_DIRECTION_RTL;
else
rq->resolved_dir = RAQM_DIRECTION_LTR;
runs = malloc (sizeof (_raqm_bidi_run) * (*run_count)); runs = malloc (sizeof (_raqm_bidi_run) * (*run_count));
if (runs) if (runs)
@ -1418,10 +1691,10 @@ _raqm_bidi_itemize (raqm_t *rq, size_t *run_count)
rq->text_len, &par_type, rq->text_len, &par_type,
levels); levels);
if (par_type == FRIBIDI_PAR_LTR) if (par_type == FRIBIDI_PAR_RTL)
rq->resolved_dir = RAQM_DIRECTION_LTR;
else
rq->resolved_dir = RAQM_DIRECTION_RTL; rq->resolved_dir = RAQM_DIRECTION_RTL;
else
rq->resolved_dir = RAQM_DIRECTION_LTR;
if (max_level == 0) if (max_level == 0)
goto done; goto done;
@ -1447,22 +1720,15 @@ _raqm_itemize (raqm_t *rq)
bool ok = true; bool ok = true;
#ifdef RAQM_TESTING #ifdef RAQM_TESTING
switch (rq->base_dir) static char *dir_names[] = {
{ "DEFAULT",
case RAQM_DIRECTION_RTL: "RTL",
RAQM_TEST ("Direction is: RTL\n\n"); "LTR",
break; "TTB"
case RAQM_DIRECTION_LTR: };
RAQM_TEST ("Direction is: LTR\n\n");
break; assert (rq->base_dir < sizeof (dir_names));
case RAQM_DIRECTION_TTB: RAQM_TEST ("Direction is: %s\n\n", dir_names[rq->base_dir]);
RAQM_TEST ("Direction is: TTB\n\n");
break;
case RAQM_DIRECTION_DEFAULT:
default:
RAQM_TEST ("Direction is: DEFAULT\n\n");
break;
}
#endif #endif
if (!_raqm_resolve_scripts (rq)) if (!_raqm_resolve_scripts (rq))
@ -1483,9 +1749,9 @@ _raqm_itemize (raqm_t *rq)
runs->len = rq->text_len; runs->len = rq->text_len;
runs->level = 0; runs->level = 0;
} }
} else {
runs = _raqm_bidi_itemize (rq, &run_count);
} }
else
runs = _raqm_bidi_itemize (rq, &run_count);
if (!runs) if (!runs)
{ {
@ -1494,6 +1760,9 @@ _raqm_itemize (raqm_t *rq)
} }
#ifdef RAQM_TESTING #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 ("Number of runs before script itemization: %zu\n\n", run_count);
RAQM_TEST ("BiDi Runs:\n"); RAQM_TEST ("BiDi Runs:\n");
@ -1617,7 +1886,8 @@ done:
} }
/* Stack to handle script detection */ /* Stack to handle script detection */
typedef struct { typedef struct
{
size_t capacity; size_t capacity;
size_t size; size_t size;
int *pair_index; int *pair_index;
@ -1910,15 +2180,47 @@ _raqm_shape (raqm_t *rq)
{ {
FT_Matrix matrix; FT_Matrix matrix;
hb_glyph_info_t *info;
hb_glyph_position_t *pos; hb_glyph_position_t *pos;
unsigned int len; unsigned int len;
FT_Get_Transform (hb_ft_font_get_face (run->font), &matrix, NULL); FT_Get_Transform (hb_ft_font_get_face (run->font), &matrix, NULL);
pos = hb_buffer_get_glyph_positions (run->buffer, &len); 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++) 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_advance, &pos[i].y_advance, matrix);
_raqm_ft_transform (&pos[i].x_offset, &pos[i].y_offset, 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 */ /* Convert index from UTF-8 to UTF-32 */
static uint32_t static size_t
_raqm_u8_to_u32_index (raqm_t *rq, _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 *s = (const unsigned char *) rq->text_utf8;
const unsigned char *t = s; const unsigned char *t = s;
@ -1982,9 +2284,64 @@ _raqm_u8_to_u32_index (raqm_t *rq,
return length; return length;
} }
static bool /* Count equivalent UTF-16 short in codepoint */
_raqm_allowed_grapheme_boundary (hb_codepoint_t l_char, static size_t
hb_codepoint_t r_char); _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 static bool
_raqm_in_hangul_syllable (hb_codepoint_t ch); _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. * character is left-to-right, then the cursor will be at the right of it.
* *
* Return value: * Return value:
* %true if the process was successful, %false otherwise. * `true` if the process was successful, `false` otherwise.
* *
* Since: 0.2 * Since: 0.2
*/ */
@ -2018,8 +2375,7 @@ raqm_index_to_position (raqm_t *rq,
if (rq == NULL) if (rq == NULL)
return false; return false;
if (rq->text_utf8) *index = _raqm_encoding_to_u32_index (rq, *index);
*index = _raqm_u8_to_u32_index (rq, *index);
if (*index >= rq->text_len) if (*index >= rq->text_len)
return false; return false;
@ -2077,6 +2433,8 @@ raqm_index_to_position (raqm_t *rq,
found: found:
if (rq->text_utf8) if (rq->text_utf8)
*index = _raqm_u32_to_u8_index (rq, *index); *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); RAQM_TEST ("The position is %d at index %zu\n",*x ,*index);
return true; return true;
} }
@ -2093,7 +2451,7 @@ found:
* @index. * @index.
* *
* Return value: * 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 * Since: 0.2
*/ */
@ -2371,8 +2729,8 @@ raqm_version_string (void)
* Checks if library version is less than or equal the specified version. * Checks if library version is less than or equal the specified version.
* *
* Return value: * Return value:
* %true if library version is less than or equal the specfied version, %false * `true` if library version is less than or equal the specified version,
* otherwise. * `false` otherwise.
* *
* Since: 0.7 * 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. * Checks if library version is less than or equal the specified version.
* *
* Return value: * Return value:
* %true if library version is less than or equal the specfied version, %false * `true` if library version is less than or equal the specified version,
* otherwise. * `false` otherwise.
* *
* Since: 0.7 * Since: 0.7
**/ **/

View File

@ -118,6 +118,10 @@ RAQM_API bool
raqm_set_text_utf8 (raqm_t *rq, raqm_set_text_utf8 (raqm_t *rq,
const char *text, const char *text,
size_t len); size_t len);
RAQM_API bool
raqm_set_text_utf16 (raqm_t *rq,
const uint16_t *text,
size_t len);
RAQM_API bool RAQM_API bool
raqm_set_par_direction (raqm_t *rq, 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 start,
size_t len); 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_API bool
raqm_set_invisible_glyph (raqm_t *rq, raqm_set_invisible_glyph (raqm_t *rq,
int gid); int gid);

View File

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