2121from urllib .parse import unquote_plus
2222from zoneinfo import ZoneInfo
2323
24- import cairocffi as cairo
25-
2624from .datalib import TimeSeries
2725from ..utils import to_seconds
2826
27+ # Lazy import for cairocffi - only loaded when actually used in the render pipeline
28+ cairo = None
29+
30+
31+ def _get_cairo ():
32+ """Lazily import cairocffi when needed for rendering."""
33+ global cairo
34+ if cairo is None :
35+ try :
36+ import cairocffi as cairo_module
37+ cairo = cairo_module
38+ except ImportError as e :
39+ raise ImportError (
40+ "cairocffi is required for image rendering but is not installed. "
41+ "Install it with: pip install cairocffi"
42+ ) from e
43+ return cairo
44+
2945
3046INFINITY = float ('inf' )
3147
@@ -737,7 +753,7 @@ def __init__(self, **params):
737753 self .loadTemplate (params .get ('template' , 'default' ))
738754
739755 opts = self .ctx .get_font_options ()
740- opts .set_antialias (cairo .ANTIALIAS_NONE )
756+ opts .set_antialias (self . cairo .ANTIALIAS_NONE )
741757 self .ctx .set_font_options (opts )
742758
743759 self .foregroundColor = params .get ('fgcolor' , self .defaultForeground )
@@ -755,22 +771,23 @@ def __init__(self, **params):
755771
756772 def setupCairo (self , outputFormat = 'png' ):
757773 self .outputFormat = outputFormat
774+ self .cairo = _get_cairo ()
758775 if outputFormat == 'png' :
759- self .surface = cairo .ImageSurface (cairo .FORMAT_ARGB32 ,
760- self .width , self .height )
776+ self .surface = self . cairo .ImageSurface (self . cairo .FORMAT_ARGB32 ,
777+ self .width , self .height )
761778 elif outputFormat == 'svg' :
762779 self .surfaceData = BytesIO ()
763- self .surface = cairo .SVGSurface (self .surfaceData ,
764- self .width , self .height )
780+ self .surface = self . cairo .SVGSurface (self .surfaceData ,
781+ self .width , self .height )
765782 elif outputFormat == 'pdf' :
766783 self .surfaceData = BytesIO ()
767- self .surface = cairo .PDFSurface (self .surfaceData ,
768- self .width , self .height )
784+ self .surface = self . cairo .PDFSurface (self .surfaceData ,
785+ self .width , self .height )
769786 res_x , res_y = self .surface .get_fallback_resolution ()
770787 self .width = float (self .width / res_x ) * 72
771788 self .height = float (self .height / res_y ) * 72
772789 self .surface .set_size (self .width , self .height )
773- self .ctx = cairo .Context (self .surface )
790+ self .ctx = self . cairo .Context (self .surface )
774791
775792 def setColor (self , value , alpha = 1.0 , forceAlpha = False ):
776793 if isinstance (value , tuple ) and len (value ) == 3 :
@@ -1395,14 +1412,14 @@ def drawLines(self, width=None, dash=None, linecap='butt',
13951412 else :
13961413 self .ctx .set_dash ([], 0 )
13971414 self .ctx .set_line_cap ({
1398- 'butt' : cairo .LINE_CAP_BUTT ,
1399- 'round' : cairo .LINE_CAP_ROUND ,
1400- 'square' : cairo .LINE_CAP_SQUARE ,
1415+ 'butt' : self . cairo .LINE_CAP_BUTT ,
1416+ 'round' : self . cairo .LINE_CAP_ROUND ,
1417+ 'square' : self . cairo .LINE_CAP_SQUARE ,
14011418 }[linecap ])
14021419 self .ctx .set_line_join ({
1403- 'miter' : cairo .LINE_JOIN_MITER ,
1404- 'round' : cairo .LINE_JOIN_ROUND ,
1405- 'bevel' : cairo .LINE_JOIN_BEVEL ,
1420+ 'miter' : self . cairo .LINE_JOIN_MITER ,
1421+ 'round' : self . cairo .LINE_JOIN_ROUND ,
1422+ 'bevel' : self . cairo .LINE_JOIN_BEVEL ,
14061423 }[linejoin ])
14071424
14081425 # check whether there is an stacked metric
0 commit comments