def append_font(self, fontfamily, path): _path, _ = parse_fontpath(path) if path is None or os.path.isfile(_path): font = FontInfo(fontfamily, path, self.fontsize) self.fonts[font.familyname] = font else: warning('fontfile `%s` is not found: %s', fontfamily, path)
def open(url, mode='Pillow'): if hasattr(url, 'read') or isinstance(url, Image.Image): stream = url elif not urlutil.isurl(url): stream = io.open(url, 'rb') else: try: # wrap BytesIO for rewind stream stream = io.BytesIO(urlopen(url).read()) except: warning(u("Could not retrieve: %s"), url) raise IOError image = pillow_open(url, stream) if mode.lower() == 'pillow': # stream will be closed by GC return image else: # mode == 'png' try: png_image = io.BytesIO() image.save(png_image, 'PNG') if hasattr(stream, 'close'): # close() is implemented on Pillow stream.close() except: warning(u("Could not convert image: %s"), url) raise IOError png_image.seek(0) return png_image
def append_font(self, fontfamily, path): _path, _ = parse_fontpath(path) if path is None or os.path.isfile(_path): font = FontInfo(fontfamily, path, self.fontsize) self.fonts[font.familyname] = font else: warning("fontfile `%s` is not found: %s", fontfamily, path)
def on_background_changing(self, node, value): formula_env = self.get_formula_env(value) if formula_env is None: # not math uri return True warning('background = "math://..." is deprecated. ' 'use label attribute.') return self.set_formula_image_to_background(node, value, formula_env)
def init_imagedrawers(debug=False): for drawer in pkg_resources.iter_entry_points('blockdiag_imagedrawers'): try: module = drawer.load() if hasattr(module, 'setup'): module.setup(module) except Exception as exc: if debug: warning('Failed to load %s: %r' % (drawer.module_name, exc))
def validate(self): if len(self.args) == 0: self.parser.print_help() sys.exit(0) self.options.input = self.args.pop(0) if self.options.output: pass elif self.options.output == '-': self.options.output = 'output.' + self.options.type.lower() else: basename = os.path.splitext(self.options.input)[0] ext = '.%s' % self.options.type.lower() self.options.output = basename + ext self.options.type = self.options.type.upper() try: imagedraw.create(self.options.type, None, debug=self.options.debug) except: msg = "unknown format: %s" % self.options.type raise RuntimeError(msg) if self.options.size: matched = re.match('^(\d+)x(\d+)$', self.options.size) if matched: self.options.size = [int(n) for n in matched.groups()] else: msg = "--size option must be formatted as WIDTHxHEIGHT." raise RuntimeError(msg) if self.options.type == 'PDF': try: import reportlab.pdfgen.canvas reportlab.pdfgen.canvas except ImportError: msg = "could not output PDF format; Install reportlab." raise RuntimeError(msg) if self.options.ignore_pil: warning("--ignore-pil option is deprecated " "(detect automatically).") if self.options.nodoctype and self.options.type != 'SVG': msg = "--nodoctype option work in SVG images." raise RuntimeError(msg) if self.options.transparency is False and self.options.type != 'PNG': msg = "--no-transparency option work in PNG images." raise RuntimeError(msg) if self.options.config and not os.path.isfile(self.options.config): msg = "config file is not found: %s" % self.options.config raise RuntimeError(msg) if self.options.fontmap and not os.path.isfile(self.options.fontmap): msg = "fontmap file is not found: %s" % self.options.fontmap raise RuntimeError(msg)
def validate(self): if len(self.args) == 0: self.parser.print_help() sys.exit(0) self.options.input = self.args.pop(0) if self.options.output: pass elif self.options.output == '-': self.options.output = 'output.' + self.options.type.lower() else: basename = os.path.splitext(self.options.input)[0] ext = '.%s' % self.options.type.lower() self.options.output = basename + ext self.options.type = self.options.type.upper() try: imagedraw.create(self.options.type, None, debug=self.options.debug) except Exception: msg = "unknown format: %s" % self.options.type raise RuntimeError(msg) if self.options.size: matched = re.match(r'^(\d+)x(\d+)$', self.options.size) if matched: self.options.size = [int(n) for n in matched.groups()] else: msg = "--size option must be formatted as WIDTHxHEIGHT." raise RuntimeError(msg) if self.options.type == 'PDF': try: import reportlab.pdfgen.canvas reportlab.pdfgen.canvas except ImportError: msg = "could not output PDF format; Install reportlab." raise RuntimeError(msg) if self.options.ignore_pil: warning("--ignore-pil option is deprecated " "(detect automatically).") if self.options.nodoctype and self.options.type != 'SVG': msg = "--nodoctype option work in SVG images." raise RuntimeError(msg) if self.options.transparency is False and self.options.type != 'PNG': msg = "--no-transparency option work in PNG images." raise RuntimeError(msg) if self.options.config and not os.path.isfile(self.options.config): msg = "config file is not found: %s" % self.options.config raise RuntimeError(msg) if self.options.fontmap and not os.path.isfile(self.options.fontmap): msg = "fontmap file is not found: %s" % self.options.fontmap raise RuntimeError(msg)
def set_edge_layout(self, value): value = value.lower() if value in ('normal', 'flowchart'): warning("edge_layout is very experimental feature!") self.edge_layout = value else: msg = "unknown edge layout: %s" % value raise AttributeError(msg)
def set_dir(self, value): params = self.ARROW_DEF.get(value.lower()) if params is None: warning("unknown edge dir: %s", value) else: self.dir, self.style, self.asynchronous = params if self.node1 == self.node2 and self.dir in ('forward', 'back'): self.activate = False
def on_resizable_changing(self, node, value): if value.lower() not in ('true', 'false'): warning('%s is not boolean value. ignored.' % value) if value.lower() == 'true': node.resizable = True else: node.resizable = False return False
def on_label_changing(self, node, value): formula_env = self.get_formula_env(value) if formula_env is None: # not math uri return True if getattr(node, 'uses_formula_image', False): warning('formula has already been specified: %s' % value) return False node.label = "" return self.set_formula_image_to_background(node, value, formula_env)
def get_image_size(options): image_size = options.get('size', 'normal').lower() try: r = int(image_size) except: r = image_size_def.get(image_size) if r is None: warning('unknown image size: %s', image_size) r = image_size_def.get('normal') return r
def __init__(self, diagram, **kwargs): super(FormulaImagePlugin, self).__init__(diagram, **kwargs) self.default_formula_env = kwargs.get('env', DEFAULT_ENVIRONMENT) self.stylepackage = None stylefile = kwargs.get('style') if stylefile: basedir = os.path.dirname(os.path.abspath(self.config.input)) stylepath = os.path.join(basedir, stylefile) if os.path.exists(stylepath): # stylename exists on relative path from source file self.stylepackage = os.path.splitext(stylepath)[0] else: warning('stylefile not found: %s' % stylefile)
def create_formula_image(formula, formula_env, stylepackage): try: tmpdir = mkdtemp() # create source .tex file source = NamedTemporaryFile(mode='w+b', suffix='.tex', dir=tmpdir, delete=False) latex_source = get_latex_source(formula, formula_env, stylepackage) source.write(latex_source.encode('utf-8')) source.close() # execute platex try: # `-no-shell-escape` blocks to invoke any commands args = ['platex', '--interaction=nonstopmode', '-no-shell-escape', source.name] latex = Popen(args, stdout=PIPE, stderr=PIPE, cwd=tmpdir) stdout, _ = latex.communicate() if latex.returncode != 0: warning("raise LaTeX Exception:\n\n%s" % stdout.decode('utf-8')) return None except Exception as exc: if isinstance(exc, OSError) and exc.errno == ENOENT: error = 'platex command not found' else: error = exc warning("Fail to convert formula: %s (reason: %s)" % (formula, error)) return None # execute dvipng try: dvifile = source.name.replace('.tex', '.dvi') output = NamedTemporaryFile(suffix='.png') args = ['dvipng', '-gamma', '1.5', '-D', '110', '-T', 'tight', '-bg', 'Transparent', '-z0', dvifile, '-o', output.name] dvipng = Popen(args, stdout=PIPE, stderr=PIPE, cwd=tmpdir) stdout, _ = dvipng.communicate() if latex.returncode != 0: warning("raise dvipng Exception:\n\n%s" % stdout.decode('utf-8')) return None except Exception as exc: output.close() if isinstance(exc, OSError) and exc.errno == ENOENT: error = 'dvipng command not found' else: error = exc warning("Fail to convert formula: %s (reason: %s)" % (formula, error)) return None return output finally: rmtree(tmpdir)
def load(plugins, diagram, **kwargs): for name in plugins: if name in loaded_plugins: warning('plugin "%s" is already loaded. ignored.', name) return for ep in iter_entry_points('blockdiag_plugins', name): module = ep.load() loaded_plugins.append(name) if hasattr(module, 'setup'): module.setup(module, diagram, **kwargs) break else: msg = "unknown plugin: %s" % name raise AttributeError(msg)
def wand_open(url, stream): try: import wand.image except: warning("unknown image type: %s", url) raise IOError try: png_image = io.BytesIO() with wand.image.Image(file=stream) as img: img.format = 'PNG' img.save(file=png_image) png_image.seek(0) return png_image except Exception as exc: warning("Fail to convert %s to PNG: %r", url, exc) raise IOError
def find(self, element=None): fontfamily = getattr(element, "fontfamily", None) or self.default_fontfamily fontfamily = self.aliases.get(fontfamily, fontfamily) fontsize = getattr(element, "fontsize", None) or self.fontsize name = self._regulate_familyname(fontfamily) if name in self.fonts: font = self.fonts[name].duplicate() font.size = fontsize elif element is not None: warning("Unknown fontfamily: %s", fontfamily) elem = namedtuple("Font", "fontsize")(fontsize) font = self.find(elem) else: font = None return font
def find(self, element=None): fontfamily = getattr(element, 'fontfamily', None) or \ self.default_fontfamily fontfamily = self.aliases.get(fontfamily, fontfamily) fontsize = getattr(element, 'fontsize', None) or self.fontsize name = self._regulate_familyname(fontfamily) if name in self.fonts: font = self.fonts[name].duplicate() font.size = fontsize elif element is not None: warning("Unknown fontfamily: %s", fontfamily) elem = namedtuple('Font', 'fontsize')(fontsize) font = self.find(elem) else: font = None return font
def on_attr_changing(self, node, attr): if attr.name not in ('background', 'icon'): return True value = unquote(attr.value) matched = prefix.match(value) if not matched: return True code = icons.get(matched.group(1)) if code is None: warning('unknown octicon: %s', value) setattr(node, attr.name, None) return False if value not in icon_images: options = to_option(matched.group(2)) image = self.create_octicon_image(code, options) icon_images[value] = image setattr(node, attr.name, icon_images[value]) return False
def create_formula_image(self, formula): try: tmpdir = mkdtemp() # create source .tex file source = NamedTemporaryFile(mode='w+b', suffix='.tex', dir=tmpdir, delete=False) source.write((LATEX_SOURCE % formula).encode('utf-8')) source.close() # execute platex args = ['platex', '--interaction=nonstopmode', source.name] latex = Popen(args, stdout=PIPE, stderr=PIPE, cwd=tmpdir) stdout, stderr = latex.communicate() if latex.returncode != 0: warning("Fail to convert formula: %s", formula) warning("Reason: %s" % stdout) return None # execute dvipng dvifile = source.name.replace('.tex', '.dvi') output = NamedTemporaryFile(suffix='.png') args = ['dvipng', '-gamma', '1.5', '-D', '110', '-T', 'tight', '-bg', 'Transparent', '-z0', dvifile, '-o', output.name] dvipng = Popen(args, stdout=PIPE, stderr=PIPE, cwd=tmpdir) stdout, stderr = dvipng.communicate() if latex.returncode != 0: warning("Fail to convert formula: %s", formula) warning("Reason: %s" % stdout) output.close() return None return output finally: rmtree(tmpdir)
def set_default_line_color(self, color): warning("default_line_color is obsoleted; use default_linecolor") self.set_default_linecolor(color)
def set_default_text_color(self, color): warning("default_text_color is obsoleted; use default_textcolor") self.set_default_textcolor(color)
def set_background(self, value): if urlutil.isurl(value) or os.path.isfile(value): self.background = value else: warning("background image not found: %s", value)
def set_icon(self, value): if urlutil.isurl(value) or os.path.isfile(value): self.icon = value else: warning("icon image not found: %s", value)
def set_fontsize(self, value): warning("fontsize is obsoleted; use default_fontsize") self.set_default_fontsize(int(value))
def set_activation(self, value): value = value.lower() if value == 'none': self.activation = value else: warning("unknown activation style: %s", value)
def set_edge_height(self, value): warning("edge_height is obsoleted; use span_height") self.span_height = int(value)
def set_dir(self, value): params = self.ARROW_DEF.get(value.lower()) if params is None: warning("unknown edge dir: %s", value) else: self.dir, self.style, self.async = params
def __init__(self, diagram, **kwargs): super(DiagramMetrics, self).__init__(diagram, **kwargs) self.node_count = len(diagram.nodes) self.edges = diagram.edges self.separators = diagram.separators self.page_padding = [0, 0, self.cellsize * 3, 0] if diagram.edge_length: span_width = diagram.edge_length - self.node_width if span_width < 0: warning("edge_length is too short: %d", diagram.edge_length) span_width = 0 self.spreadsheet.set_span_width(0, self.span_width) self.spreadsheet.set_span_width(self.node_count, self.span_width) self.span_width = span_width for edge in diagram.edges: edge.textwidth, edge.textheight = self.edge_textsize(edge) height = self.edge_height + edge.textheight if edge.diagonal: height += self.node_height * 3 // 4 elif edge.direction == 'self': height += self.cellsize * 2 font = self.font_for(edge) if edge.leftnote: edge.leftnotesize = self.textsize(edge.leftnote, font=font) if height < edge.leftnotesize.height: height = edge.leftnotesize.height if edge.rightnote: edge.rightnotesize = self.textsize(edge.rightnote, font=font) if height < edge.rightnotesize.height: height = edge.rightnotesize.height self.spreadsheet.set_node_height(edge.order + 1, height) self.expand_pagesize_for_note(edge) span_width = defaultdict(int) span_height = defaultdict(int) for block in diagram.altblocks: x1, y1 = block.xy x2 = x1 + block.colwidth y2 = y1 + block.colheight for y in range(y1, y2): span_width[(x1, y)] += 1 span_width[(x2, y)] += 1 for x in range(x1, x2): if block.type != 'else': span_height[(x, y1)] += 1 span_height[(x, y2)] += 1 for x in range(self.node_count + 1): widths = [span_width[xy] for xy in span_width if xy[0] == x] if widths: width = self.span_width + max(widths) * self.cellsize self.spreadsheet.set_span_width(x, width) for y in range(0, len(self.edges) + 1): blocks = [b for b in diagram.altblocks if b.edges[0].order == y] span_height = self.spreadsheet.span_height[y] span_height = 0 if blocks: max_ylevel_top = max(b.ylevel_top for b in blocks) span_height = (self.spreadsheet.span_height[y + 1] + self.cellsize * 5 // 2 * (max_ylevel_top - 1) + self.cellsize) self.spreadsheet.set_span_height(y + 1, span_height) blocks = [b for b in diagram.altblocks if b.edges[-1].order == y] if blocks: max_ylevel_bottom = max(b.ylevel_bottom for b in blocks) span_height = (self.spreadsheet.span_height[y + 2] + self.cellsize // 2 * (max_ylevel_bottom - 1)) self.spreadsheet.set_span_height(y + 2, span_height)