Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add indexing operators to ObjectSet and FontSet #14

Merged
merged 3 commits into from
Sep 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 43 additions & 6 deletions src/fontconfig/fontconfig.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -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, <c_impl.FcBool>trim, &csp, &result)
# TODO: Return csp
self._ptr, p._ptr, <c_impl.FcBool>trim, NULL, &result)
# TODO: Support csp
if result == c_impl.FcResultMatch:
return FontSet(<intptr_t>ptr)
elif result == c_impl.FcResultNoMatch:
Expand Down Expand Up @@ -513,9 +511,12 @@ cdef class Pattern:
if result is NULL:
raise ValueError("Invalid format: %s" % fmt)
py_str = <bytes>(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
Expand Down Expand Up @@ -676,14 +677,37 @@ 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 <bint>c_impl.FcObjectSetAdd(self._ptr, value_)

def build(self, values: Iterable[str]) -> None:
"""Build object set from iterable"""
for value in values:
if not self.add(value):
raise MemoryError()

def __iter__(self) -> Iterator[str]:
for i in range(self._ptr.nobject):
yield <bytes>(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 <bytes>(self._ptr.objects[index]).decode("utf-8")


cdef class FontSet:
"""A FontSet simply holds a list of patterns; these are used to return
Expand Down Expand Up @@ -723,6 +747,19 @@ cdef class FontSet:
for i in range(self._ptr.nfont):
yield Pattern(<intptr_t>(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(<intptr_t>self._ptr.fonts[index], owner=False)


def query(where: str = "", select: Iterable[str] = ("family",)) -> List[Dict[str, Any]]:
"""
Expand Down
31 changes: 27 additions & 4 deletions tests/test_fontconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -263,13 +259,40 @@ 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()
object_set.build(["family", "style", "slant", "weight", "size", "aspect", "lang"])
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",
Expand Down