Skip to content

Commit 99598dc

Browse files
authored
Merge pull request #30 from chireiden/bugfix/style-booleans
Fix parsing of boolean values in style lines
2 parents 9039c7a + 24e7f63 commit 99598dc

File tree

4 files changed

+59
-28
lines changed

4 files changed

+59
-28
lines changed

src/ass/data.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ def dump(v):
9898
return ""
9999

100100
if isinstance(v, bool):
101+
# Mimics behavior of booleans in styles,
102+
# not that of field sections
103+
# where "yes" and "no" would be more appropriate.
101104
return str(-int(v))
102105

103106
if isinstance(v, timedelta):

src/ass/line.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,18 @@ class Unknown(_Line):
8080
value = _Field("Value", str, default="")
8181

8282

83+
def _parse_bool_in_style(s: str) -> bool:
84+
"""Booleans in styles are parsed as integers and compared against 0.
85+
86+
This is unlike the normal "parse_bool" behavior.
87+
It would probably be more sane to parse these as ints,
88+
but that would be a breaking change to our API.
89+
90+
https://github.com/libass/libass/blob/534a5f8299c5ab3c2782856fcb843bfea47b7afc/libass/ass.c#L602-L605
91+
"""
92+
return int(s) != 0
93+
94+
8395
class Style(_Line):
8496
""" A style line in ASS.
8597
"""
@@ -92,10 +104,10 @@ class Style(_Line):
92104
secondary_color = _Field("SecondaryColour", Color, default=Color.RED)
93105
outline_color = _Field("OutlineColour", Color, default=Color.BLACK)
94106
back_color = _Field("BackColour", Color, default=Color.BLACK)
95-
bold = _Field("Bold", bool, default=False)
96-
italic = _Field("Italic", bool, default=False)
97-
underline = _Field("Underline", bool, default=False)
98-
strike_out = _Field("StrikeOut", bool, default=False)
107+
bold = _Field("Bold", _parse_bool_in_style, default=False)
108+
italic = _Field("Italic", _parse_bool_in_style, default=False)
109+
underline = _Field("Underline", _parse_bool_in_style, default=False)
110+
strike_out = _Field("StrikeOut", _parse_bool_in_style, default=False)
99111
scale_x = _Field("ScaleX", float, default=100)
100112
scale_y = _Field("ScaleY", float, default=100)
101113
spacing = _Field("Spacing", float, default=0)

tests/test.ass

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
ScriptType: v4.00+
33
PlayResX: 500
44
PlayResY: 500
5+
WrapStyle: 0
6+
ScaledBorderAndShadow: yes
7+
YCbCr Matrix: None
58

69
[Aegisub Project Garbage]
710
Audio File: video.mkv
@@ -23,6 +26,7 @@ Another Line: 20
2326
[V4+ Styles]
2427
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
2528
Style: Default,Arial,20,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,1,2,5,10,10,10,1
29+
Style: Alternative,Arial,20,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,-1,-1,-1,-1,100,100,0,0,1,2,2,2,10,10,10,1
2630

2731
[Events]
2832
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text

tests/test_ass.py

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,43 +9,42 @@
99
import ass
1010

1111
folder = Path(__file__).parent
12+
test_ass_path = Path(folder, "test.ass")
1213

1314

14-
class TestDocument:
15+
@pytest.fixture
16+
def example_ass():
17+
with test_ass_path.open("r", encoding='utf_8_sig') as f:
18+
return f.read()
1519

16-
test_ass = Path(folder, "test.ass")
1720

18-
def test_parse_dump(self):
19-
with self.test_ass.open("r", encoding='utf_8_sig') as f:
20-
contents = f.read()
21+
@pytest.fixture
22+
def example_doc(example_ass):
23+
return ass.parse(StringIO(example_ass))
2124

22-
doc = ass.parse(StringIO(contents))
23-
out = StringIO()
24-
doc.dump_file(out)
2525

26-
assert out.getvalue() == contents
26+
class TestDocument:
27+
28+
def test_parse_dump(self, example_doc, example_ass):
29+
out = StringIO()
30+
example_doc.dump_file(out)
31+
assert out.getvalue() == example_ass
2732

28-
def test_parse_encoding(self):
29-
with self.test_ass.open("r", encoding='utf_8') as f:
33+
def test_parse_encoding_utf8(self):
34+
with test_ass_path.open("r", encoding='utf_8') as f:
3035
with pytest.raises(ValueError):
3136
ass.parse(f)
3237

33-
with self.test_ass.open("r", encoding='ascii') as f:
38+
def test_parse_encoding_ascii(self):
39+
with test_ass_path.open("r", encoding='ascii') as f:
3440
with pytest.raises(ValueError):
3541
ass.parse(f)
3642

37-
def test_dump_encoding(self):
38-
for encoding in ('utf_8_sig', 'utf-8-sig'):
39-
with self.test_ass.open("r", encoding=encoding) as f:
40-
doc = ass.parse(f)
41-
42-
with self.test_ass.open("r", encoding=encoding.upper()) as f:
43-
doc = ass.parse(f)
44-
43+
def test_dump_encoding(self, example_doc):
4544
import tempfile
4645
with tempfile.TemporaryFile(mode='w', encoding='utf_8') as f:
4746
with pytest.warns(UserWarning):
48-
doc.dump_file(f)
47+
example_doc.dump_file(f)
4948

5049

5150
class TestSections:
@@ -80,9 +79,22 @@ def test_script_info(self):
8079
assert copy["Arbitrary Field"] == "hi"
8180
assert doc.play_res_x == 500
8281

83-
@pytest.mark.skip("Unimplemented")
84-
def test_styles(self):
85-
pass
82+
def test_styles(self, example_doc):
83+
assert len(example_doc.styles) == 2
84+
default_style, alternative_style = example_doc.styles
85+
86+
assert default_style.name == "Default"
87+
assert alternative_style.name == "Alternative"
88+
89+
assert default_style.bold is False
90+
assert default_style.italic is False
91+
assert default_style.underline is False
92+
assert default_style.strike_out is False
93+
94+
assert alternative_style.bold is True
95+
assert alternative_style.italic is True
96+
assert alternative_style.underline is True
97+
assert alternative_style.strike_out is True
8698

8799
@pytest.mark.skip("Unimplemented")
88100
def test_events(self):

0 commit comments

Comments
 (0)