1717from fontTools .varLib .instancer import OverlapMode
1818from PIL import Image , ImageDraw , ImageFont
1919
20+ from fontbro .exceptions import (
21+ ArgumentError ,
22+ DataError ,
23+ OperationError ,
24+ SanitizationError ,
25+ )
2026from fontbro .flags import get_flag , set_flag
2127from fontbro .math import get_euclidean_distance
2228from fontbro .subset import parse_unicodes
@@ -229,7 +235,7 @@ def __init__(self, filepath, **kwargs):
229235 self ._init_with_ttfont (filepath , ** kwargs )
230236 else :
231237 filepath_type = type (filepath ).__name__
232- raise ValueError (
238+ raise ArgumentError (
233239 f"Invalid filepath type: expected pathlib.Path or str, found { filepath_type !r} ."
234240 )
235241
@@ -240,7 +246,7 @@ def _init_with_filepath(self, filepath, **kwargs):
240246 self ._ttfont = TTFont (self ._filepath , ** kwargs )
241247
242248 except TTLibError as error :
243- raise ValueError (f"Invalid font at filepath: { filepath !r} ." ) from error
249+ raise ArgumentError (f"Invalid font at filepath: { filepath !r} ." ) from error
244250
245251 def _init_with_fileobject (self , fileobject , ** kwargs ):
246252 try :
@@ -249,7 +255,9 @@ def _init_with_fileobject(self, fileobject, **kwargs):
249255 self ._ttfont = TTFont (self ._fileobject , ** kwargs )
250256
251257 except TTLibError as error :
252- raise ValueError (f"Invalid font at fileobject: { fileobject !r} ." ) from error
258+ raise ArgumentError (
259+ f"Invalid font at fileobject: { fileobject !r} ."
260+ ) from error
253261
254262 def _init_with_font (self , font , ** kwargs ):
255263 self ._init_with_ttfont (font .get_ttfont ())
@@ -311,7 +319,7 @@ def get_characters(self, *, ignore_blank=False):
311319 font = self .get_ttfont ()
312320 cmap = font .getBestCmap ()
313321 if cmap is None :
314- raise TypeError ("Unable to find the 'best' unicode cmap dict." )
322+ raise DataError ("Unable to find the 'best' unicode cmap dict." )
315323 glyfs = font .get ("glyf" )
316324 for code , char_name in cmap .items ():
317325 code_hex = f"{ code :04X} "
@@ -515,7 +523,7 @@ def get_fingerprint_match(self, other, *, tolerance=10, text=""):
515523 other_font = other
516524 else :
517525 other_type = type (other ).__name__
518- raise ValueError (
526+ raise ArgumentError (
519527 "Invalid other filepath/font: expected str or Font instance, "
520528 f"found { other_type !r} ."
521529 )
@@ -551,7 +559,7 @@ def get_format(self, *, ignore_flavor=False):
551559 elif version == "wOF2" :
552560 format = self .FORMAT_WOFF2
553561 if format is None :
554- raise TypeError ("Unable to get the font format." )
562+ raise DataError ("Unable to get the font format." )
555563 return format
556564
557565 def get_glyphs (self ):
@@ -644,8 +652,9 @@ def _get_name_id(cls, key):
644652 elif isinstance (key , str ):
645653 return cls ._NAMES_BY_KEY [key ]["id" ]
646654 else :
647- raise TypeError (
648- f"Invalid key type, expected int or str, found { type (key ).__name__ !r} ."
655+ key_type = type (key ).__name__
656+ raise ArgumentError (
657+ f"Invalid key type, expected int or str, found { key_type !r} ."
649658 )
650659
651660 def get_name (self , key ):
@@ -1107,8 +1116,8 @@ def rename(self, *, family_name="", style_name="", update_style_flags=True):
11071116 postscript_name = re .sub (r"[\-]+" , "-" , postscript_name ).strip ("-" )
11081117 postscript_name_length = len (postscript_name )
11091118 if postscript_name_length > 63 :
1110- raise ValueError (
1111- "PostScript name max-length ( 63 characters) exceeded "
1119+ raise ArgumentError (
1120+ "Computed PostScript name exceeded 63 characters max-length "
11121121 f" ({ postscript_name_length } characters)."
11131122 )
11141123
@@ -1165,17 +1174,18 @@ def sanitize(self, *, strict=True):
11651174 error_code = result .returncode
11661175 errors = result .stderr
11671176 if error_code :
1168- exc = Exception (
1177+ raise SanitizationError (
11691178 f"OpenType Sanitizer returned non-zero exit code ({ error_code } ): \n { errors } "
11701179 )
1171- print (exc )
1172- raise exc
1180+
11731181 elif strict :
11741182 warnings = result .stdout
11751183 success_message = "File sanitized successfully!\n "
11761184 if warnings != success_message :
11771185 warnings = warnings .rstrip (success_message )
1178- raise Exception (f"OpenType Sanitizer warnings: \n { warnings } " )
1186+ raise SanitizationError (
1187+ f"OpenType Sanitizer warnings: \n { warnings } "
1188+ )
11791189
11801190 def save (self , filepath = None , * , overwrite = False ):
11811191 """
@@ -1196,7 +1206,7 @@ def save(self, filepath=None, *, overwrite=False):
11961206 not specififed.
11971207 """
11981208 if not filepath and not self ._filepath :
1199- raise ValueError (
1209+ raise ArgumentError (
12001210 "Font doesn't have a filepath. Please specify a filepath to save to."
12011211 )
12021212
@@ -1220,7 +1230,7 @@ def save(self, filepath=None, *, overwrite=False):
12201230 filename = fsutil .join_filename (basename , extension )
12211231 filepath = fsutil .join_filepath (dirpath , filename )
12221232 if fsutil .is_file (filepath ) and not overwrite :
1223- raise ValueError (
1233+ raise ArgumentError (
12241234 f"Invalid filepath, a file already exists at { filepath !r} "
12251235 "and 'overwrite' option is 'False' (consider using 'overwrite=True')."
12261236 )
@@ -1313,7 +1323,7 @@ def save_variable_instances(
13131323 :raises TypeError: If the font is not a variable font.
13141324 """
13151325 if not self .is_variable ():
1316- raise TypeError ("Only a variable font can be instantiated." )
1326+ raise OperationError ("Only a variable font can be instantiated." )
13171327
13181328 fsutil .assert_not_file (dirpath )
13191329 fsutil .make_dirs (dirpath )
@@ -1501,7 +1511,7 @@ def subset(self, *, unicodes="", glyphs=None, text="", **options):
15011511 """
15021512 font = self .get_ttfont ()
15031513 if not any ([unicodes , glyphs , text ]):
1504- raise ValueError (
1514+ raise ArgumentError (
15051515 "Subsetting requires at least one of the following args: unicode,"
15061516 " glyphs, text."
15071517 )
@@ -1555,7 +1565,7 @@ def to_sliced_variable(self, *, coordinates, **options):
15551565 :raises ValueError: If the coordinates axes are all pinned
15561566 """
15571567 if not self .is_variable ():
1558- raise TypeError ("Only a variable font can be sliced." )
1568+ raise OperationError ("Only a variable font can be sliced." )
15591569
15601570 font = self .get_ttfont ()
15611571 coordinates = coordinates or {}
@@ -1576,10 +1586,10 @@ def to_sliced_variable(self, *, coordinates, **options):
15761586
15771587 # ensure that coordinates axes are defined and that are not all pinned
15781588 if len (coordinates_axes_tags ) == 0 :
1579- raise ValueError ("Invalid coordinates: axes not defined." )
1589+ raise ArgumentError ("Invalid coordinates: axes not defined." )
15801590 elif set (coordinates_axes_tags ) == set (self .get_variable_axes_tags ()):
15811591 if self ._all_axes_pinned (coordinates ):
1582- raise ValueError (
1592+ raise ArgumentError (
15831593 "Invalid coordinates: all axes are pinned (use to_static method)."
15841594 )
15851595
@@ -1622,19 +1632,19 @@ def to_static(
16221632 :raises ValueError: If the coordinates axes are not all pinned
16231633 """
16241634 if not self .is_variable ():
1625- raise TypeError ("Only a variable font can be made static." )
1635+ raise OperationError ("Only a variable font can be made static." )
16261636
16271637 font = self .get_ttfont ()
16281638
16291639 # take coordinates from instance with specified style name
16301640 if style_name :
16311641 if coordinates :
1632- raise ValueError (
1642+ raise ArgumentError (
16331643 "Invalid arguments: 'coordinates' and 'style_name' are mutually exclusive."
16341644 )
16351645 instance = self .get_variable_instance_by_style_name (style_name = style_name )
16361646 if not instance :
1637- raise ValueError (
1647+ raise ArgumentError (
16381648 f"Invalid style name: instance with style name { style_name !r} not found."
16391649 )
16401650 coordinates = instance ["coordinates" ].copy ()
@@ -1650,7 +1660,7 @@ def to_static(
16501660
16511661 # ensure that coordinates axes are all pinned
16521662 if not self ._all_axes_pinned (coordinates ):
1653- raise ValueError ("Invalid coordinates: all axes must be pinned." )
1663+ raise ArgumentError ("Invalid coordinates: all axes must be pinned." )
16541664
16551665 # get instance closest to coordinates
16561666 instance = self .get_variable_instance_closest_to_coordinates (coordinates )
0 commit comments