Skip to content

Commit

Permalink
Pull local_css back out of function, combine cover/titlepage checks
Browse files Browse the repository at this point in the history
  • Loading branch information
Vince committed Sep 5, 2023
1 parent c0091af commit dcd0379
Showing 1 changed file with 54 additions and 55 deletions.
109 changes: 54 additions & 55 deletions se/se_epub_lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -1047,7 +1047,7 @@ def files_not_in_spine(self) -> set:
spine_files = set(self.spine_file_paths + [self.toc_path])
return xhtml_files.difference(spine_files)

def _lint_css_checks(self, local_css: dict, local_css_path: str, local_css_rules: dict) -> tuple:
def _lint_css_checks(self, local_css_path: str, abbr_with_whitespace: list) -> tuple:
"""
Process main CSS checks
Expand All @@ -1058,37 +1058,10 @@ def _lint_css_checks(self, local_css: dict, local_css_path: str, local_css_rules
local_css_rules: Dictionary of the local CSS rules
OUTPUTS
2-tuple (messages, local_css) to be used by the lint function
messages
"""
messages = [];

# Iterate over rules to do some other checks
abbr_with_whitespace = []
for selector, rules in local_css_rules.items():
if "z3998:poem" in selector:
local_css["has_poem_style"] = True

if "z3998:verse" in selector:
local_css["has_verse_style"] = True

if "z3998:song" in selector:
local_css["has_song_style"] = True

if "z3998:hymn" in selector:
local_css["has_hymn_style"] = True

if "z3998:lyrics" in selector:
local_css["has_lyrics_style"] = True

if "span.elision" in selector:
local_css["has_elision_style"] = True

if "abbr" in selector and "nowrap" in rules:
abbr_with_whitespace.append(selector)

if regex.search(r"\[\s*xml\s*\|", selector, flags=regex.IGNORECASE) and "@namespace xml \"http://www.w3.org/XML/1998/namespace\";" not in self.local_css:
messages.append(LintMessage("c-003", "[css]\\[xml|attr][/] selector in CSS, but no XML namespace declared ([css]@namespace xml \"http://www.w3.org/XML/1998/namespace\";[/]).", se.MESSAGE_TYPE_ERROR, local_css_path))

# lxml has not implemented the following in cssselect: *:first-of-type, *:last-of-type, *:nth-of-type, *:nth-last-of-type, *:only-of-type
# BUT ONLY WHEN USED ON *. This includes for example: `section [epub|type~="test"]:first-of-type` (note the `*` is implicit)
# Therefore we can't simplify them in build or test against them.
Expand Down Expand Up @@ -1128,7 +1101,7 @@ def _lint_css_checks(self, local_css: dict, local_css_path: str, local_css_rules
if matches:
messages.append(LintMessage("c-025", "Illegal percent unit used to set [css]height[/] or positioning property. Hint: [css]vh[/] to specify vertical-oriented properties like height or position.", se.MESSAGE_TYPE_ERROR, local_css_path))

return (messages, local_css)
return messages

def _update_missing_styles(filename: str, dom: se.easy_xml.EasyXmlTree, local_css: dict) -> list:
"""
Expand Down Expand Up @@ -1232,6 +1205,23 @@ def _lint_svg_checks(filename: str, file_contents: str, svg_dom: se.easy_xml.Eas

messages = [];

if f"{os.sep}src{os.sep}" not in root:
if self.cover_path and filename.name == self.cover_path.name:
# Check that cover is in all caps
nodes = svg_dom.xpath("//text[re:test(., '[a-z]')]")
if nodes:
messages.append(LintMessage("s-002", "Lowercase letters in cover. Cover text must be all uppercase.", se.MESSAGE_TYPE_ERROR, filename, [node.to_string() for node in nodes]))
# Ensure the producer has built the cover.
# The default cover image is a white background which when encoded to base64 begins with 299 characters and then has a long string of `A`s
if svg_dom.xpath("//image[re:test(@xlink:href, '^.{299}A{50,}')]"):
messages.append(LintMessage("m-063", "Cover image has not been built.", se.MESSAGE_TYPE_ERROR, filename))

# Check that titlepage is in all caps
if filename.name == "titlepage.svg":
nodes = svg_dom.xpath("//text[re:test(., '[a-z]') and not(text()='translated by' or text()='illustrated by' or text()='and')]")
if nodes:
messages.append(LintMessage("s-003", "Lowercase letters in titlepage. Titlepage text must be all uppercase except [text]translated by[/] and [text]illustrated by[/].", se.MESSAGE_TYPE_ERROR, filename, [node.to_string() for node in nodes]))

# Make images have reasonable dimensions
viewbox = svg_dom.xpath("/svg/@viewBox", True)
if viewbox:
Expand All @@ -1242,11 +1232,6 @@ def _lint_svg_checks(filename: str, file_contents: str, svg_dom: se.easy_xml.Eas
except Exception as ex:
raise se.InvalidFileException(f"Couldn’t parse SVG [xhtml]viewBox[/] attribute in [path][link=file://{filename.resolve()}]{filename}[/][/].") from ex

# If we're looking at the cover image, ensure that the producer has built it.
# The default cover image is a white background which when encoded to base64 begins with 299 characters and then has a long string of `A`s
if self.cover_path and filename.name == self.cover_path.name and svg_dom.xpath("//image[re:test(@xlink:href, '^.{299}A{50,}')]"):
messages.append(LintMessage("m-063", "Cover image has not been built.", se.MESSAGE_TYPE_ERROR, filename))

# Check for illegal transform or id attribute
nodes = svg_dom.xpath("//*[@transform or @id]")
if nodes:
Expand All @@ -1262,6 +1247,9 @@ def _lint_svg_checks(filename: str, file_contents: str, svg_dom: se.easy_xml.Eas
if invalid_transform_attributes:
messages.append(LintMessage("x-003", "Illegal [xml]transform[/] attribute. SVGs should be optimized to remove use of [xml]transform[/]. Try using Inkscape to save as an “optimized SVG”.", se.MESSAGE_TYPE_ERROR, filename, invalid_transform_attributes))

if invalid_id_attributes:
messages.append(LintMessage("x-014", "Illegal [xml]id[/] attribute.", se.MESSAGE_TYPE_ERROR, filename, invalid_id_attributes))

# Check for fill: #000 which should simply be removed
nodes = svg_dom.xpath("//*[contains(@fill, '#000') or contains(translate(@style, ' ', ''), 'fill:#000')]")
if nodes:
Expand All @@ -1276,21 +1264,6 @@ def _lint_svg_checks(filename: str, file_contents: str, svg_dom: se.easy_xml.Eas
if match and match[0] != "viewBox":
messages.append(LintMessage("x-006", f"[xml]{match}[/] found instead of [xml]viewBox[/]. [xml]viewBox[/] must be correctly capitalized.", se.MESSAGE_TYPE_ERROR, filename))

if invalid_id_attributes:
messages.append(LintMessage("x-014", "Illegal [xml]id[/] attribute.", se.MESSAGE_TYPE_ERROR, filename, invalid_id_attributes))

if f"{os.sep}src{os.sep}" not in root:
# Check that cover and titlepage images are in all caps
if self.cover_path and filename.name == self.cover_path.name:
nodes = svg_dom.xpath("//text[re:test(., '[a-z]')]")
if nodes:
messages.append(LintMessage("s-002", "Lowercase letters in cover. Cover text must be all uppercase.", se.MESSAGE_TYPE_ERROR, filename, [node.to_string() for node in nodes]))

if filename.name == "titlepage.svg":
nodes = svg_dom.xpath("//text[re:test(., '[a-z]') and not(text()='translated by' or text()='illustrated by' or text()='and')]")
if nodes:
messages.append(LintMessage("s-003", "Lowercase letters in titlepage. Titlepage text must be all uppercase except [text]translated by[/] and [text]illustrated by[/].", se.MESSAGE_TYPE_ERROR, filename, [node.to_string() for node in nodes]))

return messages

def _lint_special_file_checks(filename: str, dom: se.easy_xml.EasyXmlTree, file_contents: str, ebook_flags: dict, special_file: str, self) -> list:
Expand Down Expand Up @@ -1625,7 +1598,7 @@ def _lint_xhtml_css_checks(filename: str, dom: se.easy_xml.EasyXmlTree, local_cs

return messages

def _lint_xhtml_metadata_checks(filename: str, dom: se.easy_xml.EasyXmlTree) -> list:
def _lint_xhtml_metadata_checks(filename: str, dom: se.easy_xml.EasyXmlTree, self) -> list:
"""
Helper function used in self.lint()
Process metadata checks on an .xhtml file
Expand Down Expand Up @@ -3232,9 +3205,34 @@ def lint(self, skip_lint_ignore: bool, allowed_messages: Optional[List[str]] = N
local_css_selectors = [regex.sub(r"::[\p{Lowercase_Letter}\-]+", "", selector) for selector in local_css_rules]
unused_selectors = local_css_selectors.copy()

(css_messages, local_css) = _lint_css_checks(self, local_css, local_css_path, local_css_rules)
if css_messages:
messages = messages + css_messages
# Iterate over rules to do some other checks
abbr_with_whitespace = []
for selector, rules in local_css_rules.items():
if "z3998:poem" in selector:
local_css["has_poem_style"] = True

if "z3998:verse" in selector:
local_css["has_verse_style"] = True

if "z3998:song" in selector:
local_css["has_song_style"] = True

if "z3998:hymn" in selector:
local_css["has_hymn_style"] = True

if "z3998:lyrics" in selector:
local_css["has_lyrics_style"] = True

if "span.elision" in selector:
local_css["has_elision_style"] = True

if "abbr" in selector and "nowrap" in rules:
abbr_with_whitespace.append(selector)

if regex.search(r"\[\s*xml\s*\|", selector, flags=regex.IGNORECASE) and "@namespace xml \"http://www.w3.org/XML/1998/namespace\";" not in self.local_css:
messages.append(LintMessage("c-003", "[css]\\[xml|attr][/] selector in CSS, but no XML namespace declared ([css]@namespace xml \"http://www.w3.org/XML/1998/namespace\";[/]).", se.MESSAGE_TYPE_ERROR, local_css_path))

messages = messages + _lint_css_checks(self, local_css_path, abbr_with_whitespace)

missing_files = []
if self.is_se_ebook:
Expand Down Expand Up @@ -3517,7 +3515,7 @@ def lint(self, skip_lint_ignore: bool, allowed_messages: Optional[List[str]] = N

messages = messages + _lint_xhtml_css_checks(filename, dom, local_css_path)

messages = messages + _lint_xhtml_metadata_checks(filename, dom)
messages = messages + _lint_xhtml_metadata_checks(filename, dom, self)

messages = messages + _lint_xhtml_syntax_checks(filename, dom, self, file_contents, ebook_flags, language)

Expand All @@ -3538,6 +3536,7 @@ def lint(self, skip_lint_ignore: bool, allowed_messages: Optional[List[str]] = N
if self.is_se_ebook and not ebook_flags["has_cover_source"]:
missing_files.append("images/cover.source.jpg")

# check for classes used but not in CSS, and classes only used once
missing_selectors = []
single_use_css_classes = []

Expand Down

0 comments on commit dcd0379

Please sign in to comment.