Fix bare-number/px unit inconsistency and coordinate system (2.0b1)#450
Fix bare-number/px unit inconsistency and coordinate system (2.0b1)#450
Conversation
SVG user units and px are equivalent (1 user unit = 1 px per spec §5.9.2). ReportLab works in points (1 pt = 1/72 in; 1 px = 0.75 pt at 96 dpi). Previously, `convertLength` mixed user-unit and point semantics: bare numbers were treated as points (1:1), while `px` was already scaled ×0.75. This made `font-size="13"` and `font-size="13px"` produce different sizes, and caused incorrect physical drawing dimensions. The fix applies the px→pt conversion at the correct architectural level: - `convertLength` now returns **user units** for all length types: - bare numbers → 1:1 (unchanged, already user units) - `px` → 1:1 (was ×0.75) - `pt` → ×(96/72) ≈ ×1.333 - `pc` → ×16 (1 pc = 12 pt = 16 px) - `mm`/`cm`/`in` → toLength(x) / PX_TO_PT - A new `convertLengthToPt` method (= `convertLength × PX_TO_PT`) is used for values that must be absolute points in ReportLab: font-size (which bypasses group transforms) and SVG element canvas dimensions. - `get_box` parses `viewBox` as raw floats (unitless coordinate space per spec §8.1) rather than through `convertLength`. - The viewport scale in `renderSvg` is now `canvas_pt / view_box_user_units`, which naturally incorporates the ×0.75 factor. This ensures path data coordinates (which bypass `convertLength`) and shape attributes are handled consistently by the same transform. - `DEFAULT_FONT_SIZE` updated from 12 (pt) to 16 (user units = px), which equals the same 12 pt after px→pt conversion. Results: - `font-size="13"` and `font-size="13px"` both produce 9.75 pt ✓ - `font-size="13pt"` stays 13.0 pt ✓ - SVG drawings are physically correct: a 150×40 px SVG produces a 112.5×30 pt Drawing ✓ - Flags and other path-heavy SVGs render without cropping ✓ Fixes #439, addresses #433.
- DEFAULT_FONT_SIZE is now 12 pt (CSS default) instead of 16 user units; em_base defaults use DEFAULT_FONT_SIZE / PX_TO_PT = 16 user units - font-size defaults use "12pt" string so convertLength parses them correctly - SVGs with no explicit width/height but a viewBox now fall back to viewBox dimensions for scale, matching the Drawing size set in render(); fixes cropping of files like fill-rule-nonzero.svg - test_units_svg assertion updated: em is measured in user units (16), not pt
|
I tried this changes and they solve the problem I had with the font size. The transformation from SVG to PDF works well. But the new factor The function def setFont(self, font, fontSize):
if self._font != font or self._fontSize != fontSize:
self._font = font
self._fontSize = fontSize
style = self.style
for k in TEXT_STYLES:
if k in style:
del style[k]
svgAttrs = self.fontHacks[font] if font in self.fontHacks else {}
if isinstance(font,RLString):
svgAttrs.update(iter(font.svgAttrs.items()))
if svgAttrs:
for k,v in svgAttrs.items():
a = 'font-'+k
if a in TEXT_STYLES:
style[a] = v
if 'font-family' not in style:
style['font-family'] = font
style['font-size'] = '%spx' % fontSizeIn the last line the point to pixel ratio is My initial example in #433 shows this behavior. The initial font size is It is great the the one direction now works as expected. Once again I want to thank you for your work. |
|
Re: font size shrinkage in SVG round-trip This is a known limitation caused by a bug in ReportLab's # reportlab/graphics/renderSVG.py
style['font-size'] = '%spx' % fontSize # should be 'pt', not 'px'Minimal reproduction with ReportLab 4.4.3: from reportlab.graphics.shapes import Drawing, String
from reportlab.graphics import renderSVG
# A string at 9.75pt (equivalent to 13px at 0.75 scale)
d = Drawing(200, 100)
d.add(String(10, 50, "Hello", fontSize=9.75))
svg_out = renderSVG.drawToString(d)
print(svg_out.decode())
# Output contains: font-size: 9.75px
# But 9.75 is in points — should be font-size: 9.75pt (or font-size: 13px)The fix in ReportLab would be to change the last line of style['font-size'] = '%spt' % fontSizeThis is outside svglib's control. The |
|
@deeplook Thanks for you summary. I will send a mail to a reportlab author and link your comment to fix also this issue. |
New test module tests/test_units.py covers the end-to-end Drawing dimension and font-size behaviour for all SVG length unit types (bare numbers, px, pt, mm, cm, in), making the 96 dpi → 72 dpi conversion contract explicit and regression-proof. README.rst gains a "Units and output sizes" section explaining the px→pt factor, a reference table, font-size implications, and guidance on which unit to use for web-only, print, and mixed workflows.
|
@deeplook, I'm sorry, but I will clearly be less active on this module, so don't count on me for reviews in the future. Thanks a LOT for all you are doing 🏅 |
Fixes #439.
Summary
SVG spec §5.9.2 defines 1 user unit = 1 px = 0.75 pt (at 96 dpi). Previous
releases treated 1 user unit as 1 pt, causing
font-size="13"andfont-size="13px"to render at different sizes, and all output to be 33%larger than a browser would produce.
Changes
convertLengthnow returns user units (px) instead of ReportLab points.convertLengthToPthelper multiplies byPX_TO_PT = 0.75for the fewplaces that need absolute points (font sizes, canvas dimensions).
get_boxparsesviewBoxas raw floats — never applies unit conversion(viewBox defines a unitless coordinate space, SVG spec §8.1).
renderSvgviewport scale =canvas_pts / viewBox_user_units, whichnaturally incorporates ×0.75 for all shape coordinates via the group
transform.
viewBoxbut no explicitwidth/heightnow fall back toviewBox dimensions for the scale, fixing cropping of such files.
DEFAULT_FONT_SIZErestored to12(pt, the CSS default);embases useDEFAULT_FONT_SIZE / PX_TO_PT = 16user units.convertLengthlevel and the renderedfontSizelevel.Output PDF sizes will differ from 1.x: any SVG whose dimensions are expressed
in user units or
pxwill produce a document that is 75% of its previouslinear size (correct physical size per spec).
Migration — to restore 1.x dimensions:
See
CHANGELOG.rstfor full details.Related issues filed
preserveAspectRationot implemented (pre-existing, unrelated)rem/vw/vhnot yet supported (future work)