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

StrutStyle for leading/lineheight adjustment on ParagraphStyle + TextStyle.setLetterSpacing and TextStyle.setWordSpacing #299

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
31 changes: 31 additions & 0 deletions src/skia/Paragraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ void initParagraph(py::module &m) {
py::class_<FontCollection, sk_sp<FontCollection>, SkRefCnt> font_collection(m, "textlayout_FontCollection");
py::class_<ParagraphBuilder> paragraph_builder(m, "textlayout_ParagraphBuilder");
py::class_<ParagraphStyle> paragraph_style(m, "textlayout_ParagraphStyle");
py::class_<StrutStyle> strut_style(m, "textlayout_StrutStyle");
py::class_<TextStyle> text_style(m, "textlayout_TextStyle");
py::class_<Paragraph> paragraph(m, "textlayout_Paragraph");

Expand Down Expand Up @@ -82,6 +83,20 @@ paragraph_builder
.def("Build", &ParagraphBuilder::Build)
;

strut_style
.def(py::init())
.def("setStrutEnabled",
py::overload_cast<const bool>(&StrutStyle::setStrutEnabled),
R"docstring(
)docstring",
py::arg("strutenabled"))
.def("setLeading",
py::overload_cast<const SkScalar>(&StrutStyle::setLeading),
R"docstring(
)docstring",
py::arg("leading"))
;

paragraph_style
.def(py::init())
.def("setTextStyle",
Expand All @@ -94,6 +109,11 @@ paragraph_style
R"docstring(
)docstring",
py::arg("align"))
.def("setStrutStyle",
py::overload_cast<StrutStyle>(&ParagraphStyle::setStrutStyle),
R"docstring(
)docstring",
py::arg("strutstyle"))
;

font_collection
Expand Down Expand Up @@ -154,6 +174,16 @@ text_style
R"docstring(
)docstring",
py::arg("locale"))
.def("setLetterSpacing",
py::overload_cast<SkScalar>(&TextStyle::setLetterSpacing),
R"docstring(
)docstring",
py::arg("letterspacing"))
.def("setWordSpacing",
py::overload_cast<SkScalar>(&TextStyle::setWordSpacing),
R"docstring(
)docstring",
py::arg("wordspacing"))
.def("setDecoration",
py::overload_cast<TextDecoration>(&TextStyle::setDecoration),
R"docstring(
Expand Down Expand Up @@ -208,6 +238,7 @@ m.attr("textlayout").attr("FontCollection") = m.attr("textlayout_FontCollection"
m.attr("textlayout").attr("ParagraphBuilder") = m.attr("textlayout_ParagraphBuilder");
m.attr("textlayout").attr("ParagraphStyle") = m.attr("textlayout_ParagraphStyle");
m.attr("textlayout").attr("Paragraph") = m.attr("textlayout_Paragraph");
m.attr("textlayout").attr("StrutStyle") = m.attr("textlayout_StrutStyle");
m.attr("textlayout").attr("TextStyle") = m.attr("textlayout_TextStyle");
m.attr("textlayout").attr("TextAlign") = m.attr("textlayout_TextAlign");
m.attr("textlayout").attr("TextDecoration") = m.attr("textlayout_TextDecoration");
Expand Down
4 changes: 4 additions & 0 deletions src/skia/TextBlob.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ py::class_<SkTextBlob::Iter::Run>(iter, "Run")

iter
.def(py::init<const SkTextBlob&>())
.def("__iter__",
[] (SkTextBlob::Iter& it) {
return it;
})
.def("__next__",
[] (SkTextBlob::Iter& it) {
SkTextBlob::Iter::Run run;
Expand Down
114 changes: 114 additions & 0 deletions tests/test_paragraph.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import skia
import pytest
import operator

@pytest.fixture(scope='session')
def textlayout_font_collection():
Expand All @@ -17,6 +18,14 @@ def test_ParagraphStyle_init0(paragraph_style):
assert isinstance(paragraph_style, skia.textlayout_ParagraphStyle)


@pytest.fixture(scope='session')
def strut_style():
return skia.textlayout.StrutStyle()

def test_StrutStyle_init0(strut_style):
assert isinstance(strut_style, skia.textlayout_StrutStyle)


@pytest.fixture(scope='session')
def textlayout_text_style():
return skia.textlayout.TextStyle()
Expand Down Expand Up @@ -61,3 +70,108 @@ def test_Paragraph_linebreak(paragraph_builder, textlayout_text_style, textlayou
paragraph = builder.Build()
paragraph.layout(300)
assert (paragraph.Height > 0) and (paragraph.Height > paragraph.LongestLine * 2)


stenson marked this conversation as resolved.
Show resolved Hide resolved
@pytest.mark.parametrize('test_operator, spec_a, spec_b', [
(operator.eq, (False, 1.0), (True, 1.0)),
(operator.eq, (True, 0), (True, 1.0)),
(operator.eq, (True, 0.5), (True, 1.0)),
(operator.lt, (True, 1.0), (True, 2.0)),
(operator.lt, (True, 2.0), (True, 3.0)),
])
def test_Paragraph_strutHeight(paragraph_builder, textlayout_text_style, textlayout_font_collection, paragraph_style, test_operator, strut_style, spec_a, spec_b):
paint = skia.Paint()
paint.setColor(skia.ColorBLACK)
paint.setAntiAlias(True)

textlayout_text_style.setFontSize(50)
textlayout_text_style.setForegroundPaint(paint)

textlayout_font_collection.setDefaultFontManager(skia.FontMgr())

def graf_with_strut(enabled, leading_factor):
strut_style.setStrutEnabled(enabled)
strut_style.setLeading(leading_factor)
paragraph_style.setStrutStyle(strut_style)

builder = skia.textlayout.ParagraphBuilder.make(
paragraph_style, textlayout_font_collection, skia.Unicodes.ICU.Make()
)
builder.pushStyle(textlayout_text_style)

builder.addText("o\no")
paragraph = builder.Build()
paragraph.layout(300)

return paragraph

paragraph_a_height = graf_with_strut(*spec_a).Height
paragraph_b_height = graf_with_strut(*spec_b).Height

assert test_operator(paragraph_a_height, paragraph_b_height)


@pytest.mark.parametrize('spacing_a, spacing_b', [
(-1, 0),
(0, 1),
(1, 2),
(2, 3),
(-1, 1),
])
def test_Paragraph_letterSpacing(paragraph_builder, textlayout_text_style, textlayout_font_collection, paragraph_style, strut_style, spacing_a, spacing_b):
paint = skia.Paint()
paint.setColor(skia.ColorBLACK)
paint.setAntiAlias(True)

textlayout_font_collection.setDefaultFontManager(skia.FontMgr())

def graf_with_letterspacing(letterspacing):
textlayout_text_style.setFontSize(50)
textlayout_text_style.setForegroundPaint(paint)
textlayout_text_style.setLetterSpacing(letterspacing)

builder = skia.textlayout.ParagraphBuilder.make(
paragraph_style, textlayout_font_collection, skia.Unicodes.ICU.Make()
)
builder.pushStyle(textlayout_text_style)

builder.addText("ooo")
paragraph = builder.Build()
paragraph.layout(300)

return paragraph

assert graf_with_letterspacing(spacing_a).LongestLine < graf_with_letterspacing(spacing_b).LongestLine


@pytest.mark.parametrize('spacing_a, spacing_b', [
(-1, 0),
(0, 1),
(1, 2),
(2, 3),
(-1, 1),
])
def test_Paragraph_wordSpacing(paragraph_builder, textlayout_text_style, textlayout_font_collection, paragraph_style, strut_style, spacing_a, spacing_b):
paint = skia.Paint()
paint.setColor(skia.ColorBLACK)
paint.setAntiAlias(True)

textlayout_font_collection.setDefaultFontManager(skia.FontMgr())

def graf_with_word_spacing(letterspacing):
textlayout_text_style.setFontSize(50)
textlayout_text_style.setForegroundPaint(paint)
textlayout_text_style.setWordSpacing(letterspacing)

builder = skia.textlayout.ParagraphBuilder.make(
paragraph_style, textlayout_font_collection, skia.Unicodes.ICU.Make()
)
builder.pushStyle(textlayout_text_style)

builder.addText("word word word")
paragraph = builder.Build()
paragraph.layout(300)

return paragraph

assert graf_with_word_spacing(spacing_a).LongestLine < graf_with_word_spacing(spacing_b).LongestLine
Loading