Coverage for src/svglib/__init__.py: 28%

47 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2026-06-16 15:14 +0200

1"""A tool for converting SVG to PDF. 

2 

3This module provides a high-level function for converting SVG files to PDF format 

4using the ReportLab graphics library. It also serves as the main entry point for 

5the svglib package. 

6 

7The module includes: 

8- svg2pdf(): Convert SVG files to PDF programmatically. 

9- main(): Command-line interface for SVG to PDF conversion. 

10- Automatic file path handling and output generation. 

11""" 

12 

13import argparse 

14import sys 

15import textwrap 

16from datetime import datetime 

17from importlib.metadata import PackageNotFoundError, version 

18from os.path import basename, dirname, exists, splitext 

19from typing import Optional 

20 

21from reportlab.graphics import renderPDF 

22 

23from svglib import svglib 

24 

25try: 

26 __version__ = version("svglib") 

27except PackageNotFoundError: 

28 __version__ = "unknown" 

29 

30 

31def svg2pdf(path: str, outputPat: Optional[str] = None) -> None: 

32 """Convert an SVG file to PDF format. 

33 

34 High-level function that loads an SVG file, converts it to a ReportLab drawing, 

35 and saves it as a PDF file. Supports both .svg and .svgz (compressed SVG) files. 

36 

37 Args: 

38 path: Path to the input SVG file (.svg or .svgz extension). 

39 outputPat: Optional output path pattern. Supports placeholders: 

40 - %(dirname)s: Directory of input file 

41 - %(basename)s: Full filename with extension 

42 - %(base)s: Filename without extension 

43 - %(ext)s: File extension 

44 - %(now)s: Current datetime object 

45 - %(format)s: Output format (always "pdf") 

46 Also supports {name} format strings. 

47 

48 Returns: 

49 None. The PDF file is written to disk. 

50 

51 Raises: 

52 FileNotFoundError: If the input SVG file does not exist. 

53 Exception: If SVG parsing or PDF generation fails. 

54 OSError: If the output directory is not writable. 

55 

56 Examples: 

57 Basic conversion: 

58 >>> svg2pdf("input.svg") # Creates "input.pdf" 

59 

60 Custom output location: 

61 >>> svg2pdf("input.svg", "output.pdf") 

62 

63 Using placeholders: 

64 >>> svg2pdf("path/file.svg", "%(dirname)s/converted/%(base)s.pdf") 

65 # Creates "path/converted/file.pdf" 

66 

67 Timestamped output: 

68 >>> svg2pdf("file.svg", "backup/%(now.year)s-%(now.month)s-%(base)s.pdf") 

69 

70 Note: 

71 The function will overwrite existing PDF files without warning. 

72 For compressed SVG files (.svgz), the file is automatically decompressed. 

73 """ 

74 

75 # derive output filename from output pattern 

76 file_info = { 

77 "dirname": dirname(path) or ".", 

78 "basename": basename(path), 

79 "base": basename(splitext(path)[0]), 

80 "ext": splitext(path)[1], 

81 "now": datetime.now(), 

82 "format": "pdf", 

83 } 

84 out_pattern = outputPat or "%(dirname)s/%(base)s.%(format)s" 

85 # allow classic %%(name)s notation 

86 out_path = out_pattern % file_info 

87 # allow also newer {name} notation 

88 out_path = out_path.format(**file_info) 

89 

90 # generate a drawing from the SVG file 

91 try: 

92 drawing = svglib.svg2rlg(path) 

93 except: 

94 print("Rendering failed.") 

95 raise 

96 

97 # save converted file 

98 if drawing: 

99 renderPDF.drawToFile(drawing, out_path, showBoundary=0) 

100 

101 

102# command-line usage stuff 

103def main() -> None: 

104 """Main entry point for the CLI.""" 

105 ext = "pdf" 

106 ext_caps = ext.upper() 

107 format_args = dict( 

108 prog=basename(sys.argv[0]), 

109 version=__version__, 

110 ts_pattern="{{dirname}}/out-" 

111 "{{now.hour}}-{{now.minute}}-{{now.second}}-" 

112 "%(base)s", 

113 ext=ext, 

114 ext_caps=ext_caps, 

115 ) 

116 format_args["ts_pattern"] += ".%s" % format_args["ext"] 

117 desc = "{prog} v. {version}\n".format(**format_args) 

118 desc += "A converter from SVG to {} (via ReportLab Graphics)\n".format(ext_caps) 

119 epilog = textwrap.dedent( 

120 """\ 

121 examples: 

122 # convert path/file.svg to path/file.{ext} 

123 {prog} path/file.svg 

124 

125 # convert file1.svg to file1.{ext} and file2.svgz to file2.{ext} 

126 {prog} file1.svg file2.svgz 

127 

128 # convert file.svg to out.{ext} 

129 {prog} -o out.{ext} file.svg 

130 

131 # convert all SVG files in path/ to PDF files with names like: 

132 # path/file1.svg -> file1.{ext} 

133 {prog} -o "%(base)s.{ext}" path/file*.svg 

134 

135 # like before but with timestamp in the PDF files: 

136 # path/file1.svg -> path/out-12-58-36-file1.{ext} 

137 {prog} -o {ts_pattern} path/file*.svg 

138 

139 issues/pull requests: 

140 https://github.com/deeplook/svglib 

141 """.format(**format_args) 

142 ) 

143 p = argparse.ArgumentParser( 

144 description=desc, 

145 epilog=epilog, 

146 formatter_class=argparse.RawDescriptionHelpFormatter, 

147 ) 

148 

149 p.add_argument( 

150 "-v", "--version", help="Print version number and exit.", action="store_true" 

151 ) 

152 

153 p.add_argument( 

154 "-o", 

155 "--output", 

156 metavar="PATH_PAT", 

157 help="Set output path (incl. the placeholders: dirname, basename," 

158 "base, ext, now) in both, %%(name)s and {name} notations.", 

159 ) 

160 

161 p.add_argument( 

162 "input", 

163 metavar="PATH", 

164 nargs="*", 

165 help="Input SVG file path with extension .svg or .svgz.", 

166 ) 

167 

168 args = p.parse_args() 

169 

170 if args.version: 

171 print(__version__) 

172 sys.exit() 

173 

174 if not args.input: 

175 p.print_usage() 

176 sys.exit() 

177 

178 paths = [a for a in args.input if exists(a)] 

179 for path in paths: 

180 svg2pdf(path, outputPat=args.output)