Skip to content
Open
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ This can also be enabled programmatically with `warnings.simplefilter('default',
### Fixed
* SVG: `currentColor` and `inherit` are now read regardless of case, like the color keywords, so `fill="currentcolor"`, as in the Font Awesome icons of many web pages, no longer fails with `ValueError: unsupported color specification currentcolor` - thanks to @RavSinghChandan
* SVG: black or gray text no longer fails with an `AssertionError` when it is in a `<tspan>` that has its own style, as in every text written by Inkscape: the check on the text color left out `DeviceGray`, the color fpdf2 uses for black and gray - thanks to @RavSinghChandan
* SVG: a `light-dark()` color, as in the icons of the Zensical documentation theme, is now read as its first, light color, which is the one CSS uses when no `color-scheme` is set, instead of failing with `ValueError: unsupported color specification` - thanks to @RavSinghChandan
* SVG: an image without an `xmlns` declaration no longer fails with `ValueError: substring not found` when it contains a tag that is not supported, such as `<title>` or `<script>`; the tag is skipped with a warning, as in an image that declares the namespace - thanks to @RavSinghChandan
* `FPDF.text()` raises a clear `ValueError` for a NaN or infinite `x` or `y`, like `FPDF.set_xy()`, instead of writing `nan` or `inf` into the page, which is not a valid PDF number - thanks to @RavSinghChandan
* SVG: `clip-path="none"`, as written by Inkscape, no longer fails with an `AssertionError`, and a `url(#id)` reference to a clip path whose id contains a `-` or a `.`, or that is quoted, now finds that clip path, instead of failing with a `KeyError` or clipping with another one whose id is the part before the `-` or `.` - thanks to @RavSinghChandan
Expand Down
33 changes: 32 additions & 1 deletion fpdf/svg.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,35 @@ def svgcolor(colorstr: str) -> DeviceRGB | DeviceGray:
raise ValueError(f"unsupported color specification {colorstr}")


def _light_color(colorstr: str) -> str:
"""
CSS Color 5: without a color-scheme, which SVG documents rarely set,
light-dark() is its first, light color. Any other value is returned as is.
"""
stripped = colorstr.strip()
if stripped.lower().startswith("light-dark(") and stripped.endswith(")"):
return _light_color(
_split_top_level_commas(stripped[len("light-dark(") : -1])[0].strip()
)
return colorstr


def _split_top_level_commas(value: str) -> list[str]:
"""Split on the commas that are not inside parentheses, as in "rgb(1, 2, 3), red"."""
parts: list[str] = []
depth, start = 0, 0
for index, character in enumerate(value):
if character == "(":
depth += 1
elif character == ")":
depth -= 1
elif character == "," and depth == 0:
parts.append(value[start:index])
start = index + 1
parts.append(value[start:])
return parts


@force_nodocument
def convert_stroke_width(incoming: str) -> Optional[float]:
val = resolve_length(incoming)
Expand Down Expand Up @@ -328,6 +357,8 @@ def optional(
@force_nodocument
def svg_paint(colorstr: Optional[str]) -> Any:
"""A fill or stroke paint: "transparent" paints nothing, like "none"."""
if colorstr:
colorstr = _light_color(colorstr)
if colorstr and colorstr.startswith("url("):
return None
if colorstr and colorstr.strip().lower() == "transparent":
Expand Down Expand Up @@ -1451,7 +1482,7 @@ def _parse_gradient_stops(
stop_color = stop_element.attrib.get("stop-color", "black")

try:
color_obj = svgcolor(stop_color)
color_obj = svgcolor(_light_color(stop_color))

color_alpha = color_obj.a if color_obj.a is not None else 1.0
effective_opacity = color_alpha * stop_opacity
Expand Down
30 changes: 30 additions & 0 deletions test/svg/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,36 @@ def Gs(**kwargs):
pytest.raises(ValueError),
id="fill color invalid",
),
pytest.param(
'<path fill="light-dark(rgb(255, 0, 0), rgba(226, 228, 233, 0.82))"/>',
Gs(fill_color="#f00"),
no_error(),
id="fill color light-dark()",
),
pytest.param(
'<path stroke="Light-Dark(#00f, white)"/>',
Gs(stroke_color="#00f"),
no_error(),
id="stroke color light-dark() uppercase",
),
pytest.param(
'<path fill="light-dark(1, red)"/>',
Gs(),
pytest.raises(ValueError),
id="fill color light-dark() invalid",
),
pytest.param(
'<path fill="light-dark(transparent, red)"/>',
Gs(fill_color=None),
no_error(),
id="fill color light-dark() transparent",
),
pytest.param(
'<path stroke="light-dark(currentColor, white)"/>',
Gs(stroke_color=GraphicsStyle.INHERIT),
no_error(),
id="stroke color light-dark() currentColor",
),
pytest.param(
'<path fill-rule="inherit"/>',
Gs(intersection_rule=GraphicsStyle.INHERIT),
Expand Down
Loading