Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/fontTools/misc/roundTools.py: 25%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

40 statements  

1""" 

2Various round-to-integer helpers. 

3""" 

4 

5import math 

6import functools 

7import logging 

8 

9log = logging.getLogger(__name__) 

10 

11__all__ = [ 

12 "noRound", 

13 "otRound", 

14 "maybeRound", 

15 "roundFunc", 

16 "nearestMultipleShortestRepr", 

17] 

18 

19 

20def noRound(value): 

21 return value 

22 

23 

24def otRound(value): 

25 """Round float value to nearest integer towards ``+Infinity``. 

26 

27 The OpenType spec (in the section on `"normalization" of OpenType Font Variations <https://docs.microsoft.com/en-us/typography/opentype/spec/otvaroverview#coordinate-scales-and-normalization>`_) 

28 defines the required method for converting floating point values to 

29 fixed-point. In particular it specifies the following rounding strategy: 

30 

31 for fractional values of 0.5 and higher, take the next higher integer; 

32 for other fractional values, truncate. 

33 

34 This function rounds the floating-point value according to this strategy 

35 in preparation for conversion to fixed-point. 

36 

37 Args: 

38 value (float): The input floating-point value. 

39 

40 Returns 

41 float: The rounded value. 

42 """ 

43 # See this thread for how we ended up with this implementation: 

44 # https://github.com/fonttools/fonttools/issues/1248#issuecomment-383198166 

45 return int(math.floor(value + 0.5)) 

46 

47 

48def maybeRound(v, tolerance, round=otRound): 

49 rounded = round(v) 

50 return rounded if abs(rounded - v) <= tolerance else v 

51 

52 

53def roundFunc(tolerance, round=otRound): 

54 if tolerance < 0: 

55 raise ValueError("Rounding tolerance must be positive") 

56 

57 if tolerance == 0: 

58 return noRound 

59 

60 if tolerance >= 0.5: 

61 return round 

62 

63 return functools.partial(maybeRound, tolerance=tolerance, round=round) 

64 

65 

66def nearestMultipleShortestRepr(value: float, factor: float) -> str: 

67 """Round to nearest multiple of factor and return shortest decimal representation. 

68 

69 This chooses the float that is closer to a multiple of the given factor while 

70 having the shortest decimal representation (the least number of fractional decimal 

71 digits). 

72 

73 For example, given the following: 

74 

75 >>> nearestMultipleShortestRepr(-0.61883544921875, 1.0/(1<<14)) 

76 '-0.61884' 

77 

78 Useful when you need to serialize or print a fixed-point number (or multiples 

79 thereof, such as F2Dot14 fractions of 180 degrees in COLRv1 PaintRotate) in 

80 a human-readable form. 

81 

82 Args: 

83 value (value): The value to be rounded and serialized. 

84 factor (float): The value which the result is a close multiple of. 

85 

86 Returns: 

87 str: A compact string representation of the value. 

88 """ 

89 if not value: 

90 return "0.0" 

91 

92 value = otRound(value / factor) * factor 

93 eps = 0.5 * factor 

94 lo = value - eps 

95 hi = value + eps 

96 # If the range of valid choices spans an integer, return the integer. 

97 if int(lo) != int(hi): 

98 return str(float(round(value))) 

99 

100 fmt = "%.8f" 

101 lo = fmt % lo 

102 hi = fmt % hi 

103 assert len(lo) == len(hi) and lo != hi 

104 for i in range(len(lo)): 

105 if lo[i] != hi[i]: 

106 break 

107 period = lo.find(".") 

108 assert period < i 

109 fmt = "%%.%df" % (i - period) 

110 return fmt % value