diff --git a/src/fontconfig/fontconfig.pyx b/src/fontconfig/fontconfig.pyx index 7002d83..8caa8ba 100644 --- a/src/fontconfig/fontconfig.pyx +++ b/src/fontconfig/fontconfig.pyx @@ -2,7 +2,6 @@ import atexit import logging from typing import Any, Dict, Iterable, Iterator, List, Tuple -from cython.cimports.libc.stdlib import free cimport fontconfig._fontconfig as c_impl @@ -239,10 +238,9 @@ cdef class Config: def font_sort(self, p: Pattern, trim: bool) -> Optional[FontSet]: """Return list of matching fonts""" cdef c_impl.FcResult result - cdef c_impl.FcCharSet* csp = NULL cdef c_impl.FcFontSet* ptr = c_impl.FcFontSort( - self._ptr, p._ptr, trim, &csp, &result) - # TODO: Return csp + self._ptr, p._ptr, trim, NULL, &result) + # TODO: Support csp if result == c_impl.FcResultMatch: return FontSet(ptr) elif result == c_impl.FcResultNoMatch: @@ -513,9 +511,12 @@ cdef class Pattern: if result is NULL: raise ValueError("Invalid format: %s" % fmt) py_str = (result).decode("utf-8") - free(result) + c_impl.FcStrFree(result) return py_str + def __repr__(self) -> str: + return dict(self).__repr__() + cdef void _ObjectToFcValue(object value, c_impl.FcValue* fc_value): assert fc_value is not NULL @@ -676,7 +677,13 @@ cdef class ObjectSet: def add(self, value: str) -> bool: """Add to an object set""" - return c_impl.FcObjectSetAdd(self._ptr, value.encode("utf-8")) + cdef bytes value_ = value.encode("utf-8") + cdef c_impl.FcObjectType* object_type = c_impl.FcNameGetObjectType(value_) + if object_type is NULL: + raise KeyError("Invalid value: %s" % value) + elif object_type.type == c_impl.FcTypeUnknown: + raise KeyError("Unknown value: %s" % value) + return c_impl.FcObjectSetAdd(self._ptr, value_) def build(self, values: Iterable[str]) -> None: """Build object set from iterable""" @@ -684,6 +691,23 @@ cdef class ObjectSet: if not self.add(value): raise MemoryError() + def __iter__(self) -> Iterator[str]: + for i in range(self._ptr.nobject): + yield (self._ptr.objects[i]).decode("utf-8") + + def __repr__(self) -> str: + return list(self).__repr__() + + def __len__(self) -> int: + return self._ptr.nobject + + def __getitem__(self, index: int) -> str: + if index >= self._ptr.nobject or index <= -self._ptr.nobject: + raise IndexError("Invalid index: %d" % index) + if index < 0: + index += self._ptr.nobject + return (self._ptr.objects[index]).decode("utf-8") + cdef class FontSet: """A FontSet simply holds a list of patterns; these are used to return @@ -723,6 +747,19 @@ cdef class FontSet: for i in range(self._ptr.nfont): yield Pattern((self._ptr.fonts[i]), owner=False) + def __repr__(self) -> str: + return list(self).__repr__() + + def __len__(self) -> int: + return self._ptr.nfont + + def __getitem__(self, index: int) -> Pattern: + if index >= self._ptr.nfont or index <= -self._ptr.nfont: + raise IndexError("Invalid index: %d" % index) + if index < 0: + index += self._ptr.nfont + return Pattern(self._ptr.fonts[index], owner=False) + def query(where: str = "", select: Iterable[str] = ("family",)) -> List[Dict[str, Any]]: """ diff --git a/tests/test_fontconfig.py b/tests/test_fontconfig.py index 2203605..d0f8145 100644 --- a/tests/test_fontconfig.py +++ b/tests/test_fontconfig.py @@ -247,10 +247,6 @@ def test_Pattern_remove() -> None: assert isinstance(pattern.remove("aspect", 0), bool) -def test_Pattern_iter(pattern: fontconfig.Pattern) -> None: - dict(pattern) - - def test_Pattern_unparse(pattern: fontconfig.Pattern) -> None: assert isinstance(pattern.unparse(), str) @@ -263,6 +259,14 @@ def test_Pattern_default_format(pattern: fontconfig.Pattern) -> None: assert isinstance(pattern.format("%{lang}"), str) +def test_Pattern_iter(pattern: fontconfig.Pattern) -> None: + dict(pattern) + + +def test_Pattern_repr(pattern) -> None: + repr(pattern) + + @pytest.fixture def object_set(): object_set = fontconfig.ObjectSet.create() @@ -270,6 +274,25 @@ def object_set(): yield object_set +def test_ObjectSet_add(object_set) -> None: + assert isinstance(object_set.add("familylang"), bool) + + +def test_ObjectSet_iter(object_set) -> None: + for item in object_set: + assert isinstance(item, str) + + +def test_ObjectSet_getitem(object_set) -> None: + for i in range(len(object_set)): + assert isinstance(object_set[i], str) + assert isinstance(object_set[-1], str) + + +def test_ObjectSet_repr(object_set) -> None: + repr(object_set) + + def test_query() -> None: result = fontconfig.query( where=":lang=en:family=Arial",