3 pygments.formatters.img |
3 pygments.formatters.img |
4 ~~~~~~~~~~~~~~~~~~~~~~~ |
4 ~~~~~~~~~~~~~~~~~~~~~~~ |
5 |
5 |
6 Formatter for Pixmap output. |
6 Formatter for Pixmap output. |
7 |
7 |
8 :copyright: Copyright 2006-2013 by the Pygments team, see AUTHORS. |
8 :copyright: Copyright 2006-2014 by the Pygments team, see AUTHORS. |
9 :license: BSD, see LICENSE for details. |
9 :license: BSD, see LICENSE for details. |
10 """ |
10 """ |
11 |
11 |
12 from __future__ import unicode_literals |
|
13 |
|
14 import sys |
12 import sys |
15 |
13 |
16 from pygments.formatter import Formatter |
14 from pygments.formatter import Formatter |
17 from pygments.util import get_bool_opt, get_int_opt, \ |
15 from pygments.util import get_bool_opt, get_int_opt, get_list_opt, \ |
18 get_list_opt, get_choice_opt |
16 get_choice_opt, xrange |
19 |
17 |
20 # Import this carefully |
18 # Import this carefully |
21 try: |
19 try: |
22 from PIL import Image, ImageDraw, ImageFont |
20 from PIL import Image, ImageDraw, ImageFont |
23 pil_available = True |
21 pil_available = True |
24 except ImportError: |
22 except ImportError: |
25 pil_available = False |
23 pil_available = False |
26 |
24 |
27 try: |
25 try: |
28 import winreg |
26 import _winreg |
29 except ImportError: |
27 except ImportError: |
30 _winreg = None |
28 try: |
|
29 import winreg as _winreg |
|
30 except ImportError: |
|
31 _winreg = None |
31 |
32 |
32 __all__ = ['ImageFormatter', 'GifImageFormatter', 'JpgImageFormatter', |
33 __all__ = ['ImageFormatter', 'GifImageFormatter', 'JpgImageFormatter', |
33 'BmpImageFormatter'] |
34 'BmpImageFormatter'] |
34 |
35 |
35 |
36 |
72 if not font_name: |
73 if not font_name: |
73 self.font_name = DEFAULT_FONT_NAME_NIX |
74 self.font_name = DEFAULT_FONT_NAME_NIX |
74 self._create_nix() |
75 self._create_nix() |
75 |
76 |
76 def _get_nix_font_path(self, name, style): |
77 def _get_nix_font_path(self, name, style): |
77 from subprocess import getstatusoutput |
78 try: |
|
79 from commands import getstatusoutput |
|
80 except ImportError: |
|
81 from subprocess import getstatusoutput |
78 exit, out = getstatusoutput('fc-list "%s:style=%s" file' % |
82 exit, out = getstatusoutput('fc-list "%s:style=%s" file' % |
79 (name, style)) |
83 (name, style)) |
80 if not exit: |
84 if not exit: |
81 lines = out.splitlines() |
85 lines = out.splitlines() |
82 if lines: |
86 if lines: |
107 def _lookup_win(self, key, basename, styles, fail=False): |
111 def _lookup_win(self, key, basename, styles, fail=False): |
108 for suffix in ('', ' (TrueType)'): |
112 for suffix in ('', ' (TrueType)'): |
109 for style in styles: |
113 for style in styles: |
110 try: |
114 try: |
111 valname = '%s%s%s' % (basename, style and ' '+style, suffix) |
115 valname = '%s%s%s' % (basename, style and ' '+style, suffix) |
112 val, _ = winreg.QueryValueEx(key, valname) |
116 val, _ = _winreg.QueryValueEx(key, valname) |
113 return val |
117 return val |
114 except EnvironmentError: |
118 except EnvironmentError: |
115 continue |
119 continue |
116 else: |
120 else: |
117 if fail: |
121 if fail: |
119 (basename, styles[0])) |
123 (basename, styles[0])) |
120 return None |
124 return None |
121 |
125 |
122 def _create_win(self): |
126 def _create_win(self): |
123 try: |
127 try: |
124 key = winreg.OpenKey( |
128 key = _winreg.OpenKey( |
125 winreg.HKEY_LOCAL_MACHINE, |
129 _winreg.HKEY_LOCAL_MACHINE, |
126 r'Software\Microsoft\Windows NT\CurrentVersion\Fonts') |
130 r'Software\Microsoft\Windows NT\CurrentVersion\Fonts') |
127 except EnvironmentError: |
131 except EnvironmentError: |
128 try: |
132 try: |
129 key = winreg.OpenKey( |
133 key = _winreg.OpenKey( |
130 winreg.HKEY_LOCAL_MACHINE, |
134 _winreg.HKEY_LOCAL_MACHINE, |
131 r'Software\Microsoft\Windows\CurrentVersion\Fonts') |
135 r'Software\Microsoft\Windows\CurrentVersion\Fonts') |
132 except EnvironmentError: |
136 except EnvironmentError: |
133 raise FontNotFound('Can\'t open Windows font registry key') |
137 raise FontNotFound('Can\'t open Windows font registry key') |
134 try: |
138 try: |
135 path = self._lookup_win(key, self.font_name, STYLES['NORMAL'], True) |
139 path = self._lookup_win(key, self.font_name, STYLES['NORMAL'], True) |
169 class ImageFormatter(Formatter): |
173 class ImageFormatter(Formatter): |
170 """ |
174 """ |
171 Create a PNG image from source code. This uses the Python Imaging Library to |
175 Create a PNG image from source code. This uses the Python Imaging Library to |
172 generate a pixmap from the source code. |
176 generate a pixmap from the source code. |
173 |
177 |
174 *New in Pygments 0.10.* |
178 .. versionadded:: 0.10 |
175 |
179 |
176 Additional options accepted: |
180 Additional options accepted: |
177 |
181 |
178 `image_format` |
182 `image_format` |
179 An image format to output to that is recognised by PIL, these include: |
183 An image format to output to that is recognised by PIL, these include: |
285 """ |
293 """ |
286 if not pil_available: |
294 if not pil_available: |
287 raise PilNotAvailable( |
295 raise PilNotAvailable( |
288 'Python Imaging Library is required for this formatter') |
296 'Python Imaging Library is required for this formatter') |
289 Formatter.__init__(self, **options) |
297 Formatter.__init__(self, **options) |
|
298 self.encoding = 'latin1' # let pygments.format() do the right thing |
290 # Read the style |
299 # Read the style |
291 self.styles = dict(self.style) |
300 self.styles = dict(self.style) |
292 if self.style.background_color is None: |
301 if self.style.background_color is None: |
293 self.background_color = '#fff' |
302 self.background_color = '#fff' |
294 else: |
303 else: |
305 self.fontw, self.fonth = self.fonts.get_char_size() |
314 self.fontw, self.fonth = self.fonts.get_char_size() |
306 # Line number options |
315 # Line number options |
307 self.line_number_fg = options.get('line_number_fg', '#886') |
316 self.line_number_fg = options.get('line_number_fg', '#886') |
308 self.line_number_bg = options.get('line_number_bg', '#eed') |
317 self.line_number_bg = options.get('line_number_bg', '#eed') |
309 self.line_number_chars = get_int_opt(options, |
318 self.line_number_chars = get_int_opt(options, |
310 'line_number_chars', 2) |
319 'line_number_chars', 2) |
311 self.line_number_bold = get_bool_opt(options, |
320 self.line_number_bold = get_bool_opt(options, |
312 'line_number_bold', False) |
321 'line_number_bold', False) |
313 self.line_number_italic = get_bool_opt(options, |
322 self.line_number_italic = get_bool_opt(options, |
314 'line_number_italic', False) |
323 'line_number_italic', False) |
315 self.line_number_pad = get_int_opt(options, 'line_number_pad', 6) |
324 self.line_number_pad = get_int_opt(options, 'line_number_pad', 6) |
316 self.line_numbers = get_bool_opt(options, 'line_numbers', True) |
325 self.line_numbers = get_bool_opt(options, 'line_numbers', True) |
317 self.line_number_separator = get_bool_opt(options, |
326 self.line_number_separator = get_bool_opt(options, |
318 'line_number_separator', True) |
327 'line_number_separator', True) |
319 self.line_number_step = get_int_opt(options, 'line_number_step', 1) |
328 self.line_number_step = get_int_opt(options, 'line_number_step', 1) |
320 self.line_number_start = get_int_opt(options, 'line_number_start', 1) |
329 self.line_number_start = get_int_opt(options, 'line_number_start', 1) |
321 if self.line_numbers: |
330 if self.line_numbers: |
322 self.line_number_width = (self.fontw * self.line_number_chars + |
331 self.line_number_width = (self.fontw * self.line_number_chars + |
323 self.line_number_pad * 2) |
332 self.line_number_pad * 2) |
324 else: |
333 else: |
325 self.line_number_width = 0 |
334 self.line_number_width = 0 |
326 self.hl_lines = [] |
335 self.hl_lines = [] |
327 hl_lines_str = get_list_opt(options, 'hl_lines', []) |
336 hl_lines_str = get_list_opt(options, 'hl_lines', []) |
328 for line in hl_lines_str: |
337 for line in hl_lines_str: |
427 # TODO: make sure tab expansion happens earlier in the chain. It |
436 # TODO: make sure tab expansion happens earlier in the chain. It |
428 # really ought to be done on the input, as to do it right here is |
437 # really ought to be done on the input, as to do it right here is |
429 # quite complex. |
438 # quite complex. |
430 value = value.expandtabs(4) |
439 value = value.expandtabs(4) |
431 lines = value.splitlines(True) |
440 lines = value.splitlines(True) |
432 #print lines |
441 # print lines |
433 for i, line in enumerate(lines): |
442 for i, line in enumerate(lines): |
434 temp = line.rstrip('\n') |
443 temp = line.rstrip('\n') |
435 if temp: |
444 if temp: |
436 self._draw_text( |
445 self._draw_text( |
437 self._get_text_pos(charno, lineno), |
446 self._get_text_pos(charno, lineno), |
452 """ |
461 """ |
453 Create drawables for the line numbers. |
462 Create drawables for the line numbers. |
454 """ |
463 """ |
455 if not self.line_numbers: |
464 if not self.line_numbers: |
456 return |
465 return |
457 for p in range(self.maxlineno): |
466 for p in xrange(self.maxlineno): |
458 n = p + self.line_number_start |
467 n = p + self.line_number_start |
459 if (n % self.line_number_step) == 0: |
468 if (n % self.line_number_step) == 0: |
460 self._draw_linenumber(p, n) |
469 self._draw_linenumber(p, n) |
461 |
470 |
462 def _paint_line_number_bg(self, im): |
471 def _paint_line_number_bg(self, im): |
468 if self.line_number_fg is None: |
477 if self.line_number_fg is None: |
469 return |
478 return |
470 draw = ImageDraw.Draw(im) |
479 draw = ImageDraw.Draw(im) |
471 recth = im.size[-1] |
480 recth = im.size[-1] |
472 rectw = self.image_pad + self.line_number_width - self.line_number_pad |
481 rectw = self.image_pad + self.line_number_width - self.line_number_pad |
473 draw.rectangle([(0, 0), |
482 draw.rectangle([(0, 0), (rectw, recth)], |
474 (rectw, recth)], |
483 fill=self.line_number_bg) |
475 fill=self.line_number_bg) |
|
476 draw.line([(rectw, 0), (rectw, recth)], fill=self.line_number_fg) |
484 draw.line([(rectw, 0), (rectw, recth)], fill=self.line_number_fg) |
477 del draw |
485 del draw |
478 |
486 |
479 def format(self, tokensource, outfile): |
487 def format(self, tokensource, outfile): |
480 """ |
488 """ |
528 class JpgImageFormatter(ImageFormatter): |
535 class JpgImageFormatter(ImageFormatter): |
529 """ |
536 """ |
530 Create a JPEG image from source code. This uses the Python Imaging Library to |
537 Create a JPEG image from source code. This uses the Python Imaging Library to |
531 generate a pixmap from the source code. |
538 generate a pixmap from the source code. |
532 |
539 |
533 *New in Pygments 1.0.* (You could create JPEG images before by passing a |
540 .. versionadded:: 1.0 |
534 suitable `image_format` option to the `ImageFormatter`.) |
|
535 """ |
541 """ |
536 |
542 |
537 name = 'img_jpg' |
543 name = 'img_jpg' |
538 aliases = ['jpg', 'jpeg'] |
544 aliases = ['jpg', 'jpeg'] |
539 filenames = ['*.jpg'] |
545 filenames = ['*.jpg'] |
543 class BmpImageFormatter(ImageFormatter): |
549 class BmpImageFormatter(ImageFormatter): |
544 """ |
550 """ |
545 Create a bitmap image from source code. This uses the Python Imaging Library to |
551 Create a bitmap image from source code. This uses the Python Imaging Library to |
546 generate a pixmap from the source code. |
552 generate a pixmap from the source code. |
547 |
553 |
548 *New in Pygments 1.0.* (You could create bitmap images before by passing a |
554 .. versionadded:: 1.0 |
549 suitable `image_format` option to the `ImageFormatter`.) |
|
550 """ |
555 """ |
551 |
556 |
552 name = 'img_bmp' |
557 name = 'img_bmp' |
553 aliases = ['bmp', 'bitmap'] |
558 aliases = ['bmp', 'bitmap'] |
554 filenames = ['*.bmp'] |
559 filenames = ['*.bmp'] |