|
18 | 18 | """
|
19 | 19 |
|
20 | 20 | from collections import namedtuple
|
21 |
| -import enum |
22 | 21 | from functools import lru_cache, partial, wraps
|
23 | 22 | import logging
|
24 | 23 | import os
|
@@ -297,186 +296,6 @@ def __init__(self, filename):
|
297 | 296 | self.depth[char] = depths[byte1 & 0xf]
|
298 | 297 |
|
299 | 298 |
|
300 |
| -PsFont = namedtuple('PsFont', 'texname psname effects encoding filename') |
301 |
| - |
302 |
| - |
303 |
| -class PsfontsMap: |
304 |
| - """ |
305 |
| - A psfonts.map formatted file, mapping TeX fonts to PS fonts. |
306 |
| -
|
307 |
| - Parameters |
308 |
| - ---------- |
309 |
| - filename : str or path-like |
310 |
| -
|
311 |
| - Notes |
312 |
| - ----- |
313 |
| - For historical reasons, TeX knows many Type-1 fonts by different |
314 |
| - names than the outside world. (For one thing, the names have to |
315 |
| - fit in eight characters.) Also, TeX's native fonts are not Type-1 |
316 |
| - but Metafont, which is nontrivial to convert to PostScript except |
317 |
| - as a bitmap. While high-quality conversions to Type-1 format exist |
318 |
| - and are shipped with modern TeX distributions, we need to know |
319 |
| - which Type-1 fonts are the counterparts of which native fonts. For |
320 |
| - these reasons a mapping is needed from internal font names to font |
321 |
| - file names. |
322 |
| -
|
323 |
| - A texmf tree typically includes mapping files called e.g. |
324 |
| - :file:`psfonts.map`, :file:`pdftex.map`, or :file:`dvipdfm.map`. |
325 |
| - The file :file:`psfonts.map` is used by :program:`dvips`, |
326 |
| - :file:`pdftex.map` by :program:`pdfTeX`, and :file:`dvipdfm.map` |
327 |
| - by :program:`dvipdfm`. :file:`psfonts.map` might avoid embedding |
328 |
| - the 35 PostScript fonts (i.e., have no filename for them, as in |
329 |
| - the Times-Bold example above), while the pdf-related files perhaps |
330 |
| - only avoid the "Base 14" pdf fonts. But the user may have |
331 |
| - configured these files differently. |
332 |
| -
|
333 |
| - Examples |
334 |
| - -------- |
335 |
| - >>> map = PsfontsMap(find_tex_file('pdftex.map')) |
336 |
| - >>> entry = map[b'ptmbo8r'] |
337 |
| - >>> entry.texname |
338 |
| - b'ptmbo8r' |
339 |
| - >>> entry.psname |
340 |
| - b'Times-Bold' |
341 |
| - >>> entry.encoding |
342 |
| - '/usr/local/texlive/2008/texmf-dist/fonts/enc/dvips/base/8r.enc' |
343 |
| - >>> entry.effects |
344 |
| - {'slant': 0.16700000000000001} |
345 |
| - >>> entry.filename |
346 |
| - """ |
347 |
| - __slots__ = ('_filename', '_unparsed', '_parsed') |
348 |
| - |
349 |
| - # Create a filename -> PsfontsMap cache, so that calling |
350 |
| - # `PsfontsMap(filename)` with the same filename a second time immediately |
351 |
| - # returns the same object. |
352 |
| - @lru_cache() |
353 |
| - def __new__(cls, filename): |
354 |
| - self = object.__new__(cls) |
355 |
| - self._filename = os.fsdecode(filename) |
356 |
| - # Some TeX distributions have enormous pdftex.map files which would |
357 |
| - # take hundreds of milliseconds to parse, but it is easy enough to just |
358 |
| - # store the unparsed lines (keyed by the first word, which is the |
359 |
| - # texname) and parse them on-demand. |
360 |
| - with open(filename, 'rb') as file: |
361 |
| - self._unparsed = {} |
362 |
| - for line in file: |
363 |
| - tfmname = line.split(b' ', 1)[0] |
364 |
| - self._unparsed.setdefault(tfmname, []).append(line) |
365 |
| - self._parsed = {} |
366 |
| - return self |
367 |
| - |
368 |
| - def __getitem__(self, texname): |
369 |
| - assert isinstance(texname, bytes) |
370 |
| - if texname in self._unparsed: |
371 |
| - for line in self._unparsed.pop(texname): |
372 |
| - if self._parse_and_cache_line(line): |
373 |
| - break |
374 |
| - try: |
375 |
| - return self._parsed[texname] |
376 |
| - except KeyError: |
377 |
| - raise LookupError( |
378 |
| - f"An associated PostScript font (required by Matplotlib) " |
379 |
| - f"could not be found for TeX font {texname.decode('ascii')!r} " |
380 |
| - f"in {self._filename!r}; this problem can often be solved by " |
381 |
| - f"installing a suitable PostScript font package in your TeX " |
382 |
| - f"package manager") from None |
383 |
| - |
384 |
| - def _parse_and_cache_line(self, line): |
385 |
| - """ |
386 |
| - Parse a line in the font mapping file. |
387 |
| -
|
388 |
| - The format is (partially) documented at |
389 |
| - http://mirrors.ctan.org/systems/doc/pdftex/manual/pdftex-a.pdf |
390 |
| - https://tug.org/texinfohtml/dvips.html#psfonts_002emap |
391 |
| - Each line can have the following fields: |
392 |
| -
|
393 |
| - - tfmname (first, only required field), |
394 |
| - - psname (defaults to tfmname, must come immediately after tfmname if |
395 |
| - present), |
396 |
| - - fontflags (integer, must come immediately after psname if present, |
397 |
| - ignored by us), |
398 |
| - - special (SlantFont and ExtendFont, only field that is double-quoted), |
399 |
| - - fontfile, encodingfile (optional, prefixed by <, <<, or <[; << always |
400 |
| - precedes a font, <[ always precedes an encoding, < can precede either |
401 |
| - but then an encoding file must have extension .enc; < and << also |
402 |
| - request different font subsetting behaviors but we ignore that; < can |
403 |
| - be separated from the filename by whitespace). |
404 |
| -
|
405 |
| - special, fontfile, and encodingfile can appear in any order. |
406 |
| - """ |
407 |
| - # If the map file specifies multiple encodings for a font, we |
408 |
| - # follow pdfTeX in choosing the last one specified. Such |
409 |
| - # entries are probably mistakes but they have occurred. |
410 |
| - # https://tex.stackexchange.com/q/10826/ |
411 |
| - |
412 |
| - if not line or line.startswith((b" ", b"%", b"*", b";", b"#")): |
413 |
| - return |
414 |
| - tfmname = basename = special = encodingfile = fontfile = None |
415 |
| - is_subsetted = is_t1 = is_truetype = False |
416 |
| - matches = re.finditer(br'"([^"]*)(?:"|$)|(\S+)', line) |
417 |
| - for match in matches: |
418 |
| - quoted, unquoted = match.groups() |
419 |
| - if unquoted: |
420 |
| - if unquoted.startswith(b"<<"): # font |
421 |
| - fontfile = unquoted[2:] |
422 |
| - elif unquoted.startswith(b"<["): # encoding |
423 |
| - encodingfile = unquoted[2:] |
424 |
| - elif unquoted.startswith(b"<"): # font or encoding |
425 |
| - word = ( |
426 |
| - # <foo => foo |
427 |
| - unquoted[1:] |
428 |
| - # < by itself => read the next word |
429 |
| - or next(filter(None, next(matches).groups()))) |
430 |
| - if word.endswith(b".enc"): |
431 |
| - encodingfile = word |
432 |
| - else: |
433 |
| - fontfile = word |
434 |
| - is_subsetted = True |
435 |
| - elif tfmname is None: |
436 |
| - tfmname = unquoted |
437 |
| - elif basename is None: |
438 |
| - basename = unquoted |
439 |
| - elif quoted: |
440 |
| - special = quoted |
441 |
| - effects = {} |
442 |
| - if special: |
443 |
| - words = reversed(special.split()) |
444 |
| - for word in words: |
445 |
| - if word == b"SlantFont": |
446 |
| - effects["slant"] = float(next(words)) |
447 |
| - elif word == b"ExtendFont": |
448 |
| - effects["extend"] = float(next(words)) |
449 |
| - |
450 |
| - # Verify some properties of the line that would cause it to be ignored |
451 |
| - # otherwise. |
452 |
| - if fontfile is not None: |
453 |
| - if fontfile.endswith((b".ttf", b".ttc")): |
454 |
| - is_truetype = True |
455 |
| - elif not fontfile.endswith(b".otf"): |
456 |
| - is_t1 = True |
457 |
| - elif basename is not None: |
458 |
| - is_t1 = True |
459 |
| - if is_truetype and is_subsetted and encodingfile is None: |
460 |
| - return |
461 |
| - if not is_t1 and ("slant" in effects or "extend" in effects): |
462 |
| - return |
463 |
| - if abs(effects.get("slant", 0)) > 1: |
464 |
| - return |
465 |
| - if abs(effects.get("extend", 0)) > 2: |
466 |
| - return |
467 |
| - |
468 |
| - if basename is None: |
469 |
| - basename = tfmname |
470 |
| - if encodingfile is not None: |
471 |
| - encodingfile = _find_tex_file(encodingfile) |
472 |
| - if fontfile is not None: |
473 |
| - fontfile = _find_tex_file(fontfile) |
474 |
| - self._parsed[tfmname] = PsFont( |
475 |
| - texname=tfmname, psname=basename, effects=effects, |
476 |
| - encoding=encodingfile, filename=fontfile) |
477 |
| - return True |
478 |
| - |
479 |
| - |
480 | 299 | def _parse_enc(path):
|
481 | 300 | r"""
|
482 | 301 | Parse a \*.enc file referenced from a psfonts.map style file.
|
|
0 commit comments