1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
| class LineBreakFormatter(ImageFormatter): def __init__(self, **options): super().__init__(**options) self.max_chars_per_line = int(options.get("max_chars_per_line", 0) or 0)
def format(self, tokensource, outfile): """ Format ``tokensource``, an iterable of ``(tokentype, tokenstring)`` tuples and write it into ``outfile``.
This implementation calculates where it should draw each token on the pixmap, then calculates the required pixmap size and draws the items. """ line_break_map = self._create_drawables(tokensource) logger.debug(f"Line break map: {line_break_map}") self._draw_line_numbers(line_break_map) sizex, sizey = self._get_image_size(self.maxlinelength, self.maxlineno) im = Image.new( "RGB", (sizex + 200, sizey), self.background_color, ) self._paint_line_number_bg(im) draw = ImageDraw.Draw(im) if self.hl_lines: x = self.image_pad + self.line_number_width - self.line_number_pad + 1 recth = self._get_line_height() rectw = im.size[0] - x for linenumber in self.hl_lines: y = self._get_line_y(linenumber - 1) draw.rectangle([(x, y), (x + rectw, y + recth)], fill=self.hl_color) for pos, value, font, text_fg, text_bg in self.drawables: if text_bg: if hasattr(draw, "textsize"): text_size = draw.textsize(text=value, font=font) else: text_size = font.getbbox(value)[2:] draw.rectangle( [pos[0], pos[1], pos[0] + text_size[0], pos[1] + text_size[1]], fill=text_bg, ) draw.text(pos, value, font=font, fill=text_fg) im.save(outfile, self.image_format.upper())
def _draw_line_numbers(self, is_line_break: List[bool]): """ Create drawables for the line numbers. """ if not self.line_numbers: return if not is_line_break: is_line_break = [False] * (self.maxlineno + 1) n = 0 for p, t in zip(range(self.maxlineno), is_line_break): if t: n += 1 if (n % self.line_number_step) == 0: self._draw_linenumber(p, n) else: self._draw_linenumber(p, " ")
def _create_drawables(self, tokensource) -> list[bool]: """ Create drawables for the token content. """ lineno = 0 charno = 0 maxcharno = 0 maxlinelength = 0 linelength = 0 is_line_break = [True] for ttype, value in tokensource: while ttype not in self.styles: ttype = ttype.parent style = self.styles[ttype] value = value.expandtabs(4) lines = value.splitlines(True) for line in lines: temp = line.rstrip("\n") while temp: if not self.max_chars_per_line: chunk = temp temp = "" else: remaining = self.max_chars_per_line - charno if remaining <= 0: linelength = 0 charno = 0 lineno += 1 is_line_break.append(False) remaining = self.max_chars_per_line if len(temp) > remaining: chunk, temp = temp[:remaining], temp[remaining:] else: chunk, temp = temp, ""
if chunk: self._draw_text( self._get_text_pos(linelength, lineno), chunk, font=self._get_style_font(style), text_fg=self._get_text_color(style), text_bg=self._get_text_bg_color(style), ) chunk_width, _ = self.fonts.get_text_size(chunk) linelength += chunk_width maxlinelength = max(maxlinelength, linelength) charno += len(chunk) maxcharno = max(maxcharno, charno)
if line.endswith("\n"): linelength = 0 charno = 0 lineno += 1 is_line_break.append(True) self.maxlinelength = maxlinelength self.maxcharno = maxcharno self.maxlineno = lineno return is_line_break
|