From f796024fe0c23a243d9d56cd8cbc58fb54df8446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20K=2E=20Sepp=C3=A4nen?= Date: Sun, 14 Jun 2015 12:52:56 +0300 Subject: [PATCH 01/10] Streamline type1font.py --- lib/matplotlib/type1font.py | 38 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/lib/matplotlib/type1font.py b/lib/matplotlib/type1font.py index ba457e840959..8c7801cca279 100644 --- a/lib/matplotlib/type1font.py +++ b/lib/matplotlib/type1font.py @@ -256,13 +256,13 @@ def _transformer(cls, tokens, slant, extend): def fontname(name): result = name if slant: - result += '_Slant_' + str(int(1000 * slant)) + result += b'_Slant_' + bytes(int(1000 * slant)) if extend != 1.0: - result += '_Extend_' + str(int(1000 * extend)) + result += b'_Extend_' + bytes(int(1000 * extend)) return result def italicangle(angle): - return str(float(angle) - np.arctan(slant) / np.pi * 180) + return bytes(float(angle) - np.arctan(slant) / np.pi * 180) def fontmatrix(array): array = array.lstrip('[').rstrip(']').strip().split() @@ -276,31 +276,31 @@ def fontmatrix(array): newmatrix = np.dot(modifier, oldmatrix) array[::2] = newmatrix[0:3, 0] array[1::2] = newmatrix[0:3, 1] - return '[' + ' '.join(str(x) for x in array) + ']' + return b'[' + ' '.join(bytes(x) for x in array) + b']' def replace(fun): def replacer(tokens): token, value = next(tokens) # name, e.g., /FontMatrix - yield value + yield bytes(value) token, value = next(tokens) # possible whitespace while token == 'whitespace': - yield value + yield bytes(value) token, value = next(tokens) if value != '[': # name/number/etc. - yield fun(value) + yield bytes(fun(value)) else: # array, e.g., [1 2 3] array = [] while value != ']': array += value token, value = next(tokens) array += value - yield fun(''.join(array)) + yield bytes(fun(''.join(array))) return replacer def suppress(tokens): for x in itertools.takewhile(lambda x: x[1] != 'def', tokens): pass - yield '' + yield b'' table = {'/FontName': replace(fontname), '/ItalicAngle': replace(italicangle), @@ -325,18 +325,10 @@ def transform(self, effects): multiplier by which the font is to be extended (so values less than 1.0 condense). Returns a new :class:`Type1Font` object. """ - buffer = io.BytesIO() - try: + with io.BytesIO() as buffer: tokenizer = self._tokens(self.parts[0]) - for value in self._transformer(tokenizer, - slant=effects.get('slant', 0.0), - extend=effects.get('extend', 1.0)): - if six.PY3 and isinstance(value, int): - value = chr(value) - value = value.encode('latin-1') - buffer.write(value) - result = buffer.getvalue() - finally: - buffer.close() - - return Type1Font((result, self.parts[1], self.parts[2])) + transformed = self._transformer(tokenizer, + slant=effects.get('slant', 0.0), + extend=effects.get('extend', 1.0)) + map(buffer.write, transformed) + return Type1Font((buffer.getvalue(), self.parts[1], self.parts[2])) From f6d6c7ad9044168688ed5287572aa97744588e4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20K=2E=20Sepp=C3=A4nen?= Date: Sun, 14 Jun 2015 15:17:58 +0300 Subject: [PATCH 02/10] Add test for type1font.py --- lib/matplotlib/tests/cmr10.pfb | Bin 0 -> 35752 bytes lib/matplotlib/tests/test_type1font.py | 53 +++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 lib/matplotlib/tests/cmr10.pfb create mode 100644 lib/matplotlib/tests/test_type1font.py diff --git a/lib/matplotlib/tests/cmr10.pfb b/lib/matplotlib/tests/cmr10.pfb new file mode 100644 index 0000000000000000000000000000000000000000..fa8c833d374ed57be70cf7b5f79bc76886b62a84 GIT binary patch literal 35752 zcma&Nby(F)*Z2*R0+Q0Tk#08K-QC@==?-b>EFD6b2xem81&JxBf>}T;ENo0H zEUZYBlyIr!H2hX>Zce<+%pM*dOeXfO zOpY#=g0u`E4{J9okSfF#;_?z=4*F#=NYTU|^0&oINR%LTD{I%k$JHDy+&oNNARu7C z&e{y(;0mm8cQA*zfPm9MYBKU5B`1i(-^%iTD=>imb2%U|6ZoI){i|{}=#be))H}fN1^`Pqd&vjsP($Is%Ri0nt4Ay*>*Eiy80_`2V-}gMKafzkL4J zF8^O{|35^M?sj&6-5Skb-txcmCid2LUjNYm9NFCs2nq#9AT%8QM^_8-E2=>>3J`N^ zcl-aDmT@xy!b8Nt5^(XqFB8OU?J8;Q2{Bi;b~Cg3+lT(TWf0(pz!7%V4iIHWSLs#U3jMC&daVGbDm-!r*^6;_W+ zcmUD$Q3NtLvywf;^6@+%w3sdaGURV$0MX(%eE`tH?w`%U0G-9}(*U2v@ACkmxg$^) zTz@|kp#0CE&Erl0r=`oU;Quc;7(g{K1H9&+5mo@!%*5q4<5&S$Qy0i@t5^YEdlR!i zlmKRzU+?(eoml~DGr-gA>>kZ#1;{NSF7`lLGPQGkR0Rw$`HcWTcl=ERpdDT9OkAxV z7XZ{E690&7zwY-p5#Uw&a{(Iw?CS31Iwe0a9M?jEqw=oCi`TvzwI* z-LQQ3ANdA0>eK(Y3h%R>^qjZMf*_#Sakl8cmNjNM+smtbulrsg#bzMw-Dm_>jD3c?ms60!2c}x zGrD*HRx@iCGk1FnJD?2x*AsXEXdt-)z2ome&I7>z70!R|4H)qlc07O)rjPpoMwtC0 z{VJf$kFf^^Ix6NzcLuWn^uLu17J&IN+k#mD(#He|W&sF)D<~`g+HYlm1z>v27GM?t z>e0=?K>x-3m=eLis@G#!fPa;)$LI!w0V=0Q6MzpI%+9}w0M%ohgTVmRV^RWx0jkFc z0fPZ5_utk6R4;!M0Vr1pKQ$LG1rE2Jl(A zJ6M{yxZB&Axc|n+`U}m@^YFvILm_J(=FegC%XY&H)1i=5*zJPyam){x~FeiZfTMGl`1X%xUV!)gL=)c<- zFeh-yzc(`AUkT@rR_0gEakX&!SLpzA0q72<|1yyafd5zD!v(#vTmbm* ztqm6d{zvb_1z`Wt{BQxt|J(iiN>2Yr`vbuLtN-B!!2fK3z}x`#pB)gG8^Hdv1p;#e z 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 z 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 z%0D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 R@9tvnWa?{a1{DDO{{Zn0fcgLc literal 0 HcmV?d00001 diff --git a/lib/matplotlib/tests/test_type1font.py b/lib/matplotlib/tests/test_type1font.py new file mode 100644 index 000000000000..81800c69f269 --- /dev/null +++ b/lib/matplotlib/tests/test_type1font.py @@ -0,0 +1,53 @@ +from __future__ import (absolute_import, division, print_function, + unicode_literals) + +import six + +from nose.tools import assert_equal, assert_in +import matplotlib.type1font as t1f +import os.path +import difflib +import hashlib + +def sha1(data): + hash = hashlib.sha1() + hash.update(data) + return hash.hexdigest() + +def test_Type1Font(): + filename = os.path.join(os.path.dirname(__file__), 'cmr10.pfb') + font = t1f.Type1Font(filename) + slanted = font.transform({'slant': 1}) + condensed = font.transform({'extend': 0.5}) + assert_equal(map(sha1, font.parts), + ['f4ce890d648e67d413a91b3109fe67a732ced96f', + 'af46adb6c528956580c125c6abf3b5eb9983bbc1', + 'e2538a88a810bc207cfa1194b658ee8967042db8']) + assert_equal(font.parts[1:], slanted.parts[1:]) + assert_equal(font.parts[1:], condensed.parts[1:]) + + differ = difflib.Differ() + diff = set(differ.compare(font.parts[0].splitlines(), + slanted.parts[0].splitlines())) + for line in ( + # Removes UniqueID + '- FontDirectory/CMR10 known{/CMR10 findfont dup/UniqueID known{dup', + '+ FontDirectory/CMR10 known{/CMR10 findfont dup', + # Alters FontMatrix + '- /FontMatrix [0.001 0 0 0.001 0 0 ]readonly def', + '+ /FontMatrix [0.001 0.0 0.001 0.001 0.0 0.0]readonly def', + # Alters ItalicAngle + '- /ItalicAngle 0 def', + '+ /ItalicAngle -45.0 def'): + assert_in(line, diff, 'diff to slanted font must contain %s' % line) + + diff = set(differ.compare(font.parts[0].splitlines(), + condensed.parts[0].splitlines())) + for line in ( + # Removes UniqueID + '- FontDirectory/CMR10 known{/CMR10 findfont dup/UniqueID known{dup', + '+ FontDirectory/CMR10 known{/CMR10 findfont dup', + # Alters FontMatrix + '- /FontMatrix [0.001 0 0 0.001 0 0 ]readonly def', + '+ /FontMatrix [0.0005 0.0 0.0 0.001 0.0 0.0]readonly def'): + assert_in(line, diff, 'diff to condensed font must contain %s' % line) From dcc2f40c67d5e960512dd3040c63eb715061844f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20K=2E=20Sepp=C3=A4nen?= Date: Sun, 14 Jun 2015 15:21:10 +0300 Subject: [PATCH 03/10] Fixes to type1font.py Slanting and extending had been broken --- lib/matplotlib/type1font.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/type1font.py b/lib/matplotlib/type1font.py index 8c7801cca279..f041f7ef68d9 100644 --- a/lib/matplotlib/type1font.py +++ b/lib/matplotlib/type1font.py @@ -283,7 +283,7 @@ def replacer(tokens): token, value = next(tokens) # name, e.g., /FontMatrix yield bytes(value) token, value = next(tokens) # possible whitespace - while token == 'whitespace': + while token is cls._whitespace: yield bytes(value) token, value = next(tokens) if value != '[': # name/number/etc. @@ -309,7 +309,7 @@ def suppress(tokens): while True: token, value = next(tokens) - if token == 'name' and value in table: + if token is cls._name and value in table: for value in table[value](itertools.chain([(token, value)], tokens)): yield value From 3b4b79db6a8c0dfa3bca447f775af53358359f32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20K=2E=20Sepp=C3=A4nen?= Date: Sun, 14 Jun 2015 15:26:58 +0300 Subject: [PATCH 04/10] PEP8 --- lib/matplotlib/tests/test_type1font.py | 32 ++++++++++++++------------ 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/lib/matplotlib/tests/test_type1font.py b/lib/matplotlib/tests/test_type1font.py index 81800c69f269..28f19620b4c8 100644 --- a/lib/matplotlib/tests/test_type1font.py +++ b/lib/matplotlib/tests/test_type1font.py @@ -9,11 +9,13 @@ import difflib import hashlib + def sha1(data): hash = hashlib.sha1() hash.update(data) return hash.hexdigest() + def test_Type1Font(): filename = os.path.join(os.path.dirname(__file__), 'cmr10.pfb') font = t1f.Type1Font(filename) @@ -30,24 +32,24 @@ def test_Type1Font(): diff = set(differ.compare(font.parts[0].splitlines(), slanted.parts[0].splitlines())) for line in ( - # Removes UniqueID - '- FontDirectory/CMR10 known{/CMR10 findfont dup/UniqueID known{dup', - '+ FontDirectory/CMR10 known{/CMR10 findfont dup', - # Alters FontMatrix - '- /FontMatrix [0.001 0 0 0.001 0 0 ]readonly def', - '+ /FontMatrix [0.001 0.0 0.001 0.001 0.0 0.0]readonly def', - # Alters ItalicAngle - '- /ItalicAngle 0 def', - '+ /ItalicAngle -45.0 def'): + # Removes UniqueID + '- FontDirectory/CMR10 known{/CMR10 findfont dup/UniqueID known{dup', + '+ FontDirectory/CMR10 known{/CMR10 findfont dup', + # Alters FontMatrix + '- /FontMatrix [0.001 0 0 0.001 0 0 ]readonly def', + '+ /FontMatrix [0.001 0.0 0.001 0.001 0.0 0.0]readonly def', + # Alters ItalicAngle + '- /ItalicAngle 0 def', + '+ /ItalicAngle -45.0 def'): assert_in(line, diff, 'diff to slanted font must contain %s' % line) diff = set(differ.compare(font.parts[0].splitlines(), condensed.parts[0].splitlines())) for line in ( - # Removes UniqueID - '- FontDirectory/CMR10 known{/CMR10 findfont dup/UniqueID known{dup', - '+ FontDirectory/CMR10 known{/CMR10 findfont dup', - # Alters FontMatrix - '- /FontMatrix [0.001 0 0 0.001 0 0 ]readonly def', - '+ /FontMatrix [0.0005 0.0 0.0 0.001 0.0 0.0]readonly def'): + # Removes UniqueID + '- FontDirectory/CMR10 known{/CMR10 findfont dup/UniqueID known{dup', + '+ FontDirectory/CMR10 known{/CMR10 findfont dup', + # Alters FontMatrix + '- /FontMatrix [0.001 0 0 0.001 0 0 ]readonly def', + '+ /FontMatrix [0.0005 0.0 0.0 0.001 0.0 0.0]readonly def'): assert_in(line, diff, 'diff to condensed font must contain %s' % line) From 66d974a65d77e88a305eb0e8b2c049d3719c8a84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20K=2E=20Sepp=C3=A4nen?= Date: Sun, 14 Jun 2015 22:28:35 +0300 Subject: [PATCH 05/10] Improve test, fix an off-by-one and simplify the code --- lib/matplotlib/tests/test_type1font.py | 19 ++++++------------- lib/matplotlib/type1font.py | 14 ++++++-------- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/lib/matplotlib/tests/test_type1font.py b/lib/matplotlib/tests/test_type1font.py index 28f19620b4c8..f40cb5936266 100644 --- a/lib/matplotlib/tests/test_type1font.py +++ b/lib/matplotlib/tests/test_type1font.py @@ -7,13 +7,6 @@ import matplotlib.type1font as t1f import os.path import difflib -import hashlib - - -def sha1(data): - hash = hashlib.sha1() - hash.update(data) - return hash.hexdigest() def test_Type1Font(): @@ -21,16 +14,16 @@ def test_Type1Font(): font = t1f.Type1Font(filename) slanted = font.transform({'slant': 1}) condensed = font.transform({'extend': 0.5}) - assert_equal(map(sha1, font.parts), - ['f4ce890d648e67d413a91b3109fe67a732ced96f', - 'af46adb6c528956580c125c6abf3b5eb9983bbc1', - 'e2538a88a810bc207cfa1194b658ee8967042db8']) + rawdata = open(filename, 'rb').read() + assert_equal(font.parts[0], rawdata[0x0006:0x10c5]) + assert_equal(font.parts[1], rawdata[0x10cb:0x897f]) + assert_equal(font.parts[2], rawdata[0x8985:0x8ba6]) assert_equal(font.parts[1:], slanted.parts[1:]) assert_equal(font.parts[1:], condensed.parts[1:]) differ = difflib.Differ() - diff = set(differ.compare(font.parts[0].splitlines(), - slanted.parts[0].splitlines())) + diff = set(differ.compare(font.parts[0].decode('latin-1').splitlines(), + slanted.parts[0].decode('latin-1').splitlines())) for line in ( # Removes UniqueID '- FontDirectory/CMR10 known{/CMR10 findfont dup/UniqueID known{dup', diff --git a/lib/matplotlib/type1font.py b/lib/matplotlib/type1font.py index f041f7ef68d9..22abae00efc9 100644 --- a/lib/matplotlib/type1font.py +++ b/lib/matplotlib/type1font.py @@ -29,6 +29,7 @@ from six.moves import filter from six import unichr +import binascii import io import itertools import numpy as np @@ -92,8 +93,7 @@ def _read(self, file): if type == 1: # ASCII text: include verbatim data += segment elif type == 2: # binary data: encode in hexadecimal - data += b''.join([('%02x' % ord(char)).encode('ascii') - for char in segment]) + data += binascii.hexlify(segment) elif type == 3: # end of file break else: @@ -123,9 +123,8 @@ def _split(self, data): # zeros backward idx = data.rindex(b'cleartomark') - 1 zeros = 512 - while zeros and ord(data[idx]) in ( - ord(b'0'[0]), ord(b'\n'[0]), ord(b'\r'[0])): - if ord(data[idx]) == ord(b'0'[0]): + while zeros and data[idx] in b'0\n\r': + if data[idx] in b'0': zeros -= 1 idx -= 1 if zeros: @@ -136,10 +135,9 @@ def _split(self, data): # but if we read a pfa file, this part is already in hex, and # I am not quite sure if even the pfb format guarantees that # it will be in binary). - binary = b''.join([unichr(int(data[i:i + 2], 16)).encode('latin-1') - for i in range(len1, idx, 2)]) + binary = binascii.unhexlify(data[len1:idx+1]) - return data[:len1], binary, data[idx:] + return data[:len1], binary, data[idx+1:] _whitespace_re = re.compile(br'[\0\t\r\014\n ]+') _token_re = re.compile(br'/{0,2}[^]\0\t\r\v\n ()<>{}/%[]+') From 372b4025aaa72ab12758c4ebe760231620fee461 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20K=2E=20Sepp=C3=A4nen?= Date: Sun, 14 Jun 2015 22:39:46 +0300 Subject: [PATCH 06/10] Fix bytes/str handling all over type1font and its test Add a test for changing the font name (this wasn't working before) --- lib/matplotlib/tests/test_type1font.py | 14 +++++--- lib/matplotlib/type1font.py | 49 +++++++++++++------------- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/lib/matplotlib/tests/test_type1font.py b/lib/matplotlib/tests/test_type1font.py index f40cb5936266..37cfb8df09ca 100644 --- a/lib/matplotlib/tests/test_type1font.py +++ b/lib/matplotlib/tests/test_type1font.py @@ -22,12 +22,15 @@ def test_Type1Font(): assert_equal(font.parts[1:], condensed.parts[1:]) differ = difflib.Differ() - diff = set(differ.compare(font.parts[0].decode('latin-1').splitlines(), - slanted.parts[0].decode('latin-1').splitlines())) + diff = list(differ.compare(font.parts[0].decode('latin-1').splitlines(), + slanted.parts[0].decode('latin-1').splitlines())) for line in ( # Removes UniqueID '- FontDirectory/CMR10 known{/CMR10 findfont dup/UniqueID known{dup', '+ FontDirectory/CMR10 known{/CMR10 findfont dup', + # Changes the font name + '- /FontName /CMR10 def', + '+ /FontName /CMR10_Slant_1000 def', # Alters FontMatrix '- /FontMatrix [0.001 0 0 0.001 0 0 ]readonly def', '+ /FontMatrix [0.001 0.0 0.001 0.001 0.0 0.0]readonly def', @@ -36,12 +39,15 @@ def test_Type1Font(): '+ /ItalicAngle -45.0 def'): assert_in(line, diff, 'diff to slanted font must contain %s' % line) - diff = set(differ.compare(font.parts[0].splitlines(), - condensed.parts[0].splitlines())) + diff = list(differ.compare(font.parts[0].decode('latin-1').splitlines(), + condensed.parts[0].decode('latin-1').splitlines())) for line in ( # Removes UniqueID '- FontDirectory/CMR10 known{/CMR10 findfont dup/UniqueID known{dup', '+ FontDirectory/CMR10 known{/CMR10 findfont dup', + # Changes the font name + '- /FontName /CMR10 def', + '+ /FontName /CMR10_Extend_500 def', # Alters FontMatrix '- /FontMatrix [0.001 0 0 0.001 0 0 ]readonly def', '+ /FontMatrix [0.0005 0.0 0.0 0.001 0.0 0.0]readonly def'): diff --git a/lib/matplotlib/type1font.py b/lib/matplotlib/type1font.py index 22abae00efc9..b9a944fa545e 100644 --- a/lib/matplotlib/type1font.py +++ b/lib/matplotlib/type1font.py @@ -164,7 +164,7 @@ def _tokens(cls, text): if match: yield (cls._whitespace, match.group()) pos += match.end() - elif text[pos] == '(': + elif text[pos] == b'(': start = pos pos += 1 depth = 1 @@ -173,19 +173,19 @@ def _tokens(cls, text): if match is None: return pos += match.end() - if match.group() == '(': + if match.group() == b'(': depth += 1 - elif match.group() == ')': + elif match.group() == b')': depth -= 1 else: # a backslash - skip the next character pos += 1 yield (cls._string, text[start:pos]) - elif text[pos:pos + 2] in ('<<', '>>'): + elif text[pos:pos + 2] in (b'<<', b'>>'): yield (cls._delimiter, text[pos:pos + 2]) pos += 2 - elif text[pos] == '<': + elif text[pos] == b'<': start = pos - pos += text[pos:].index('>') + pos += text[pos:].index(b'>') yield (cls._string, text[start:pos]) else: match = cls._token_re.match(text[pos:]) @@ -254,16 +254,16 @@ def _transformer(cls, tokens, slant, extend): def fontname(name): result = name if slant: - result += b'_Slant_' + bytes(int(1000 * slant)) + result += b'_Slant_' + str(int(1000 * slant)).encode('latin-1') if extend != 1.0: - result += b'_Extend_' + bytes(int(1000 * extend)) + result += b'_Extend_' + str(int(1000 * extend)).encode('latin-1') return result def italicangle(angle): - return bytes(float(angle) - np.arctan(slant) / np.pi * 180) + return str(float(angle) - np.arctan(slant) / np.pi * 180).encode('latin-1') def fontmatrix(array): - array = array.lstrip('[').rstrip(']').strip().split() + array = array.lstrip(b'[').rstrip(b']').strip().split() array = [float(x) for x in array] oldmatrix = np.eye(3, 3) oldmatrix[0:3, 0] = array[::2] @@ -274,7 +274,8 @@ def fontmatrix(array): newmatrix = np.dot(modifier, oldmatrix) array[::2] = newmatrix[0:3, 0] array[1::2] = newmatrix[0:3, 1] - return b'[' + ' '.join(bytes(x) for x in array) + b']' + as_string = u'[' + u' '.join(str(x) for x in array) + u']' + return as_string.encode('latin-1') def replace(fun): def replacer(tokens): @@ -284,26 +285,26 @@ def replacer(tokens): while token is cls._whitespace: yield bytes(value) token, value = next(tokens) - if value != '[': # name/number/etc. + if value != b'[': # name/number/etc. yield bytes(fun(value)) - else: # array, e.g., [1 2 3] - array = [] - while value != ']': - array += value + else: # array, e.g., [1 2 3] + result = b'' + while value != b']': + result += value token, value = next(tokens) - array += value - yield bytes(fun(''.join(array))) + result += value + yield fun(result) return replacer def suppress(tokens): - for x in itertools.takewhile(lambda x: x[1] != 'def', tokens): + for x in itertools.takewhile(lambda x: x[1] != b'def', tokens): pass yield b'' - table = {'/FontName': replace(fontname), - '/ItalicAngle': replace(italicangle), - '/FontMatrix': replace(fontmatrix), - '/UniqueID': suppress} + table = {b'/FontName': replace(fontname), + b'/ItalicAngle': replace(italicangle), + b'/FontMatrix': replace(fontmatrix), + b'/UniqueID': suppress} while True: token, value = next(tokens) @@ -328,5 +329,5 @@ def transform(self, effects): transformed = self._transformer(tokenizer, slant=effects.get('slant', 0.0), extend=effects.get('extend', 1.0)) - map(buffer.write, transformed) + list(map(buffer.write, transformed)) return Type1Font((buffer.getvalue(), self.parts[1], self.parts[2])) From 301986a4dda8ad2e35e806f33948e7bae25c1328 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20K=2E=20Sepp=C3=A4nen?= Date: Sun, 14 Jun 2015 23:48:10 +0300 Subject: [PATCH 07/10] Attempt to document the _whitespace etc objects a bit --- lib/matplotlib/type1font.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/type1font.py b/lib/matplotlib/type1font.py index b9a944fa545e..3ab4693c44d6 100644 --- a/lib/matplotlib/type1font.py +++ b/lib/matplotlib/type1font.py @@ -144,7 +144,7 @@ def _split(self, data): _comment_re = re.compile(br'%[^\r\n\v]*') _instring_re = re.compile(br'[()\\]') - # token types + # token types, compared via object identity (poor man's enum) _whitespace = object() _name = object() _string = object() From edae4ea2215017e143af2fa0a573e576c79a1a28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20K=2E=20Sepp=C3=A4nen?= Date: Sun, 14 Jun 2015 23:56:25 +0300 Subject: [PATCH 08/10] License for cmr10.pfb --- LICENSE/LICENSE_AMSFONTS | 240 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 240 insertions(+) create mode 100644 LICENSE/LICENSE_AMSFONTS diff --git a/LICENSE/LICENSE_AMSFONTS b/LICENSE/LICENSE_AMSFONTS new file mode 100644 index 000000000000..3627bb9bb617 --- /dev/null +++ b/LICENSE/LICENSE_AMSFONTS @@ -0,0 +1,240 @@ +The cmr10.pfb file is a Type-1 version of one of Knuth's Computer Modern fonts. +It is included here as test data only, but the following license applies. + +Copyright (c) 1997, 2009, American Mathematical Society (http://www.ams.org). +All Rights Reserved. + +"cmb10" is a Reserved Font Name for this Font Software. +"cmbsy10" is a Reserved Font Name for this Font Software. +"cmbsy5" is a Reserved Font Name for this Font Software. +"cmbsy6" is a Reserved Font Name for this Font Software. +"cmbsy7" is a Reserved Font Name for this Font Software. +"cmbsy8" is a Reserved Font Name for this Font Software. +"cmbsy9" is a Reserved Font Name for this Font Software. +"cmbx10" is a Reserved Font Name for this Font Software. +"cmbx12" is a Reserved Font Name for this Font Software. +"cmbx5" is a Reserved Font Name for this Font Software. +"cmbx6" is a Reserved Font Name for this Font Software. +"cmbx7" is a Reserved Font Name for this Font Software. +"cmbx8" is a Reserved Font Name for this Font Software. +"cmbx9" is a Reserved Font Name for this Font Software. +"cmbxsl10" is a Reserved Font Name for this Font Software. +"cmbxti10" is a Reserved Font Name for this Font Software. +"cmcsc10" is a Reserved Font Name for this Font Software. +"cmcsc8" is a Reserved Font Name for this Font Software. +"cmcsc9" is a Reserved Font Name for this Font Software. +"cmdunh10" is a Reserved Font Name for this Font Software. +"cmex10" is a Reserved Font Name for this Font Software. +"cmex7" is a Reserved Font Name for this Font Software. +"cmex8" is a Reserved Font Name for this Font Software. +"cmex9" is a Reserved Font Name for this Font Software. +"cmff10" is a Reserved Font Name for this Font Software. +"cmfi10" is a Reserved Font Name for this Font Software. +"cmfib8" is a Reserved Font Name for this Font Software. +"cminch" is a Reserved Font Name for this Font Software. +"cmitt10" is a Reserved Font Name for this Font Software. +"cmmi10" is a Reserved Font Name for this Font Software. +"cmmi12" is a Reserved Font Name for this Font Software. +"cmmi5" is a Reserved Font Name for this Font Software. +"cmmi6" is a Reserved Font Name for this Font Software. +"cmmi7" is a Reserved Font Name for this Font Software. +"cmmi8" is a Reserved Font Name for this Font Software. +"cmmi9" is a Reserved Font Name for this Font Software. +"cmmib10" is a Reserved Font Name for this Font Software. +"cmmib5" is a Reserved Font Name for this Font Software. +"cmmib6" is a Reserved Font Name for this Font Software. +"cmmib7" is a Reserved Font Name for this Font Software. +"cmmib8" is a Reserved Font Name for this Font Software. +"cmmib9" is a Reserved Font Name for this Font Software. +"cmr10" is a Reserved Font Name for this Font Software. +"cmr12" is a Reserved Font Name for this Font Software. +"cmr17" is a Reserved Font Name for this Font Software. +"cmr5" is a Reserved Font Name for this Font Software. +"cmr6" is a Reserved Font Name for this Font Software. +"cmr7" is a Reserved Font Name for this Font Software. +"cmr8" is a Reserved Font Name for this Font Software. +"cmr9" is a Reserved Font Name for this Font Software. +"cmsl10" is a Reserved Font Name for this Font Software. +"cmsl12" is a Reserved Font Name for this Font Software. +"cmsl8" is a Reserved Font Name for this Font Software. +"cmsl9" is a Reserved Font Name for this Font Software. +"cmsltt10" is a Reserved Font Name for this Font Software. +"cmss10" is a Reserved Font Name for this Font Software. +"cmss12" is a Reserved Font Name for this Font Software. +"cmss17" is a Reserved Font Name for this Font Software. +"cmss8" is a Reserved Font Name for this Font Software. +"cmss9" is a Reserved Font Name for this Font Software. +"cmssbx10" is a Reserved Font Name for this Font Software. +"cmssdc10" is a Reserved Font Name for this Font Software. +"cmssi10" is a Reserved Font Name for this Font Software. +"cmssi12" is a Reserved Font Name for this Font Software. +"cmssi17" is a Reserved Font Name for this Font Software. +"cmssi8" is a Reserved Font Name for this Font Software. +"cmssi9" is a Reserved Font Name for this Font Software. +"cmssq8" is a Reserved Font Name for this Font Software. +"cmssqi8" is a Reserved Font Name for this Font Software. +"cmsy10" is a Reserved Font Name for this Font Software. +"cmsy5" is a Reserved Font Name for this Font Software. +"cmsy6" is a Reserved Font Name for this Font Software. +"cmsy7" is a Reserved Font Name for this Font Software. +"cmsy8" is a Reserved Font Name for this Font Software. +"cmsy9" is a Reserved Font Name for this Font Software. +"cmtcsc10" is a Reserved Font Name for this Font Software. +"cmtex10" is a Reserved Font Name for this Font Software. +"cmtex8" is a Reserved Font Name for this Font Software. +"cmtex9" is a Reserved Font Name for this Font Software. +"cmti10" is a Reserved Font Name for this Font Software. +"cmti12" is a Reserved Font Name for this Font Software. +"cmti7" is a Reserved Font Name for this Font Software. +"cmti8" is a Reserved Font Name for this Font Software. +"cmti9" is a Reserved Font Name for this Font Software. +"cmtt10" is a Reserved Font Name for this Font Software. +"cmtt12" is a Reserved Font Name for this Font Software. +"cmtt8" is a Reserved Font Name for this Font Software. +"cmtt9" is a Reserved Font Name for this Font Software. +"cmu10" is a Reserved Font Name for this Font Software. +"cmvtt10" is a Reserved Font Name for this Font Software. +"euex10" is a Reserved Font Name for this Font Software. +"euex7" is a Reserved Font Name for this Font Software. +"euex8" is a Reserved Font Name for this Font Software. +"euex9" is a Reserved Font Name for this Font Software. +"eufb10" is a Reserved Font Name for this Font Software. +"eufb5" is a Reserved Font Name for this Font Software. +"eufb7" is a Reserved Font Name for this Font Software. +"eufm10" is a Reserved Font Name for this Font Software. +"eufm5" is a Reserved Font Name for this Font Software. +"eufm7" is a Reserved Font Name for this Font Software. +"eurb10" is a Reserved Font Name for this Font Software. +"eurb5" is a Reserved Font Name for this Font Software. +"eurb7" is a Reserved Font Name for this Font Software. +"eurm10" is a Reserved Font Name for this Font Software. +"eurm5" is a Reserved Font Name for this Font Software. +"eurm7" is a Reserved Font Name for this Font Software. +"eusb10" is a Reserved Font Name for this Font Software. +"eusb5" is a Reserved Font Name for this Font Software. +"eusb7" is a Reserved Font Name for this Font Software. +"eusm10" is a Reserved Font Name for this Font Software. +"eusm5" is a Reserved Font Name for this Font Software. +"eusm7" is a Reserved Font Name for this Font Software. +"lasy10" is a Reserved Font Name for this Font Software. +"lasy5" is a Reserved Font Name for this Font Software. +"lasy6" is a Reserved Font Name for this Font Software. +"lasy7" is a Reserved Font Name for this Font Software. +"lasy8" is a Reserved Font Name for this Font Software. +"lasy9" is a Reserved Font Name for this Font Software. +"lasyb10" is a Reserved Font Name for this Font Software. +"lcircle1" is a Reserved Font Name for this Font Software. +"lcirclew" is a Reserved Font Name for this Font Software. +"lcmss8" is a Reserved Font Name for this Font Software. +"lcmssb8" is a Reserved Font Name for this Font Software. +"lcmssi8" is a Reserved Font Name for this Font Software. +"line10" is a Reserved Font Name for this Font Software. +"linew10" is a Reserved Font Name for this Font Software. +"msam10" is a Reserved Font Name for this Font Software. +"msam5" is a Reserved Font Name for this Font Software. +"msam6" is a Reserved Font Name for this Font Software. +"msam7" is a Reserved Font Name for this Font Software. +"msam8" is a Reserved Font Name for this Font Software. +"msam9" is a Reserved Font Name for this Font Software. +"msbm10" is a Reserved Font Name for this Font Software. +"msbm5" is a Reserved Font Name for this Font Software. +"msbm6" is a Reserved Font Name for this Font Software. +"msbm7" is a Reserved Font Name for this Font Software. +"msbm8" is a Reserved Font Name for this Font Software. +"msbm9" is a Reserved Font Name for this Font Software. +"wncyb10" is a Reserved Font Name for this Font Software. +"wncyi10" is a Reserved Font Name for this Font Software. +"wncyr10" is a Reserved Font Name for this Font Software. +"wncysc10" is a Reserved Font Name for this Font Software. +"wncyss10" is a Reserved Font Name for this Font Software. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. From ce7d782ab6e53c20d2bab83169b60b5b707576de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20K=2E=20Sepp=C3=A4nen?= Date: Mon, 15 Jun 2015 00:14:50 +0300 Subject: [PATCH 09/10] PEP8 --- lib/matplotlib/tests/test_type1font.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/tests/test_type1font.py b/lib/matplotlib/tests/test_type1font.py index 37cfb8df09ca..751571f93a8e 100644 --- a/lib/matplotlib/tests/test_type1font.py +++ b/lib/matplotlib/tests/test_type1font.py @@ -22,8 +22,9 @@ def test_Type1Font(): assert_equal(font.parts[1:], condensed.parts[1:]) differ = difflib.Differ() - diff = list(differ.compare(font.parts[0].decode('latin-1').splitlines(), - slanted.parts[0].decode('latin-1').splitlines())) + diff = list(differ.compare( + font.parts[0].decode('latin-1').splitlines(), + slanted.parts[0].decode('latin-1').splitlines())) for line in ( # Removes UniqueID '- FontDirectory/CMR10 known{/CMR10 findfont dup/UniqueID known{dup', From 2e421beb8610721dbc459fe6f781b6c1d8e413b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20K=2E=20Sepp=C3=A4nen?= Date: Sun, 28 Jun 2015 11:30:09 +0300 Subject: [PATCH 10/10] Skip end-of-lines before block of zeros --- lib/matplotlib/type1font.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/type1font.py b/lib/matplotlib/type1font.py index 3ab4693c44d6..afd541a5fa2f 100644 --- a/lib/matplotlib/type1font.py +++ b/lib/matplotlib/type1font.py @@ -123,7 +123,7 @@ def _split(self, data): # zeros backward idx = data.rindex(b'cleartomark') - 1 zeros = 512 - while zeros and data[idx] in b'0\n\r': + while zeros and data[idx] in b'0' or data[idx] in b'\r\n': if data[idx] in b'0': zeros -= 1 idx -= 1