Server IP : 127.0.0.2 / Your IP : 3.145.71.192 Web Server : Apache/2.4.18 (Ubuntu) System : User : www-data ( ) PHP Version : 7.0.33-0ubuntu0.16.04.16 Disable Function : disk_free_space,disk_total_space,diskfreespace,dl,exec,fpaththru,getmyuid,getmypid,highlight_file,ignore_user_abord,leak,listen,link,opcache_get_configuration,opcache_get_status,passthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,php_uname,phpinfo,posix_ctermid,posix_getcwd,posix_getegid,posix_geteuid,posix_getgid,posix_getgrgid,posix_getgrnam,posix_getgroups,posix_getlogin,posix_getpgid,posix_getpgrp,posix_getpid,posix,_getppid,posix_getpwnam,posix_getpwuid,posix_getrlimit,posix_getsid,posix_getuid,posix_isatty,posix_kill,posix_mkfifo,posix_setegid,posix_seteuid,posix_setgid,posix_setpgid,posix_setsid,posix_setuid,posix_times,posix_ttyname,posix_uname,pclose,popen,proc_open,proc_close,proc_get_status,proc_nice,proc_terminate,shell_exec,source,show_source,system,virtual MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : ON Directory : /usr/lib/python2.7/dist-packages/pychart/ |
Upload File : |
# # Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) # # Jockey is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 2, or (at your option) any # later version. # # Jockey is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # import color import string import pychart_util import re import theme import afm.dir __doc__ = """The module for manipulating texts and their attributes. Pychart supports extensive sets of attributes in texts. All attributes are specified via "escape sequences", starting from letter "/". For example, the below examples draws string "Hello" using a 12-point font at 60-degree angle: /12/a60{}Hello List of attributes: /hA Specifies horizontal alignment of the text. A is one of L (left alignment), R (right alignment), or C (center alignment). /vA Specifies vertical alignment of the text. A is one of "B" (bottom), "T" (top), " M" (middle). /F{FONT} Switch to FONT font family. /T Shorthand of /F{Times-Roman}. /H Shorthand of /F{Helvetica}. /C Shorthand of /F{Courier}. /B Shorthand of /F{Bookman-Demi}. /A Shorthand of /F{AvantGarde-Book}. /P Shorthand of /F{Palatino}. /S Shorthand of /F{Symbol}. /b Switch to bold typeface. /i Switch to italic typeface. /o Switch to oblique typeface. /DD Set font size to DD points. /20{}2001 space odyssey! /cDD Set gray-scale to 0.DD. Gray-scale of 00 means black, 99 means white. //, /{, /} Display `/', `{', or `}'. { ... } Limit the effect of escape sequences. For example, the below example draws "Foo" at 12pt, "Bar" at 8pt, and "Baz" at 12pt. /12Foo{/8Bar}Baz \n Break the line. """ # List of fonts for which their absence have been already warned. _undefined_font_warned = {} class FontException(Exception): def __init__(self, msg, str): self.__msg = msg self.__str = str def __str__(self): return """%s (got "%s"). Write "//". "/{", "/}" to display "/", "{", "}", respectively.""" % \ (self.__msg, self.__str) def _intern_afm(font, text): global _undefined_font_warned r = afm.dir.afm.get(font, None) if r: return r font2 = _font_aliases.get(font, None) if font2: r = afm.dir.afm.get(font2, None) if r: return r try: exec("import pychart.afm.%s" % re.sub("-", "_", font)) return afm.dir.afm[font] except: if not font2 and not _undefined_font_warned.has_key(font): pychart_util.warn('Warning: unknown font "%s" while parsing "%s"' % (font, text)) _undefined_font_warned[font] = 1 if font2: try: exec('import pychart.afm.%s' % re.sub('-', '_', font2)) return afm.dir.afm[font2] except: if not _undefined_font_warned.has_key(font): pychart_util.warn('Warning: unknown font "%s" while parsing "%s"' % (font, text)) _undefined_font_warned[font] = 1 return None def line_width(font, size, text): table = _intern_afm(font, text) if not table: return 0 width = 0 for ch in text: code = ord(ch) if code < len(table): width += table[code] else: # Invalid char. Make up a number. width += table[0] width = float(width) * size / 1000.0 return width _font_family_map = {'T': 'Times', 'H': 'Helvetica', 'C': 'Courier', 'N': 'Helvetica-Narrow', 'B': 'Bookman-Demi', 'A': 'AvantGarde-Book', 'P': 'Palatino', 'S': 'Symbol'} # Aliases for ghostscript font names. _font_aliases = { 'Bookman-Demi': 'URWBookmanL-DemiBold%I', 'Bookman-DemiItalic': 'URWBookmanL-DemiBoldItal', 'Bookman-Demi-Italic': 'URWBookmanL-DemiBoldItal', 'Bookman-Light': 'URWBookmanL-Ligh', 'Bookman-LightItalic': 'URWBookmanL-LighItal', 'Bookman-Light-Italic': 'URWBookmanL-LighItal', 'Courier': 'NimbusMonL-Regu', 'Courier-Oblique': 'NimbusMonL-ReguObli', 'Courier-Bold': 'NimbusMonL-Bold', 'Courier-BoldOblique': 'NimbusMonL-BoldObli', 'AvantGarde-Book': 'URWGothicL-Book', 'AvantGarde-BookOblique': 'URWGothicL-BookObli', 'AvantGarde-Book-Oblique': 'URWGothicL-BookObli', 'AvantGarde-Demi': 'URWGothicL-Demi', 'AvantGarde-DemiOblique': 'URWGothicL-DemiObli', 'AvantGarde-Demi-Oblique': 'URWGothicL-DemiObli', 'Helvetica': 'NimbusSanL-Regu', 'Helvetica-Oblique': 'NimbusSanL-ReguItal', 'Helvetica-Bold': 'NimbusSanL-Bold', 'Helvetica-BoldOblique': 'NimbusSanL-BoldItal', 'Helvetica-Narrow': 'NimbusSanL-ReguCond', 'Helvetica-Narrow-Oblique': 'NimbusSanL-ReguCondItal', 'Helvetica-Narrow-Bold': 'NimbusSanL-BoldCond', 'Helvetica-Narrow-BoldOblique': 'NimbusSanL-BoldCondItal', 'Palatino-Roman': 'URWPalladioL-Roma', 'Palatino': 'URWPalladioL-Roma', 'Palatino-Italic': 'URWPalladioL-Ital', 'Palatino-Bold': 'URWPalladioL-Bold', 'Palatino-BoldItalic': 'URWPalladioL-BoldItal', 'NewCenturySchlbk-Roman': 'CenturySchL-Roma', 'NewCenturySchlbk': 'CenturySchL-Roma', 'NewCenturySchlbk-Italic': 'CenturySchL-Ital', 'NewCenturySchlbk-Bold': 'CenturySchL-Bold', 'NewCenturySchlbk-BoldItalic': 'CenturySchL-BoldItal', 'Times-Roman': 'NimbusRomNo9L-Regu', 'Times': 'NimbusRomNo9L-Regu', 'Times-Italic': 'NimbusRomNo9L-ReguItal', 'Times-Bold': 'NimbusRomNo9L-Medi', 'Times-BoldItalic': 'NimbusRomNo9L-MediItal', 'Symbol': 'StandardSymL', 'ZapfChancery-MediumItalic': 'URWChanceryL-MediItal', 'ZapfChancery-Medium-Italic': 'URWChanceryL-MediItal', 'ZapfDingbats': 'Dingbats' } class text_state: def copy(self): ts = text_state() ts.family = self.family ts.modifiers = list(self.modifiers) ts.size = self.size ts.line_height = self.line_height ts.color = self.color ts.halign = self.halign ts.valign = self.valign ts.angle = self.angle return ts def __init__(self): self.family = theme.default_font_family self.modifiers = [] # 'b' for bold, 'i' for italic, 'o' for oblique. self.size = theme.default_font_size self.line_height = theme.default_line_height or theme.default_font_size self.color = color.default self.halign = theme.default_font_halign self.valign = theme.default_font_valign self.angle = theme.default_font_angle class text_iterator: def __init__(self, s): self.str = unicode(s) self.i = 0 self.ts = text_state() self.stack = [] def reset(self, s): self.str = unicode(s) self.i = 0 def __return_state(self, ts, str): font_name = ts.family if ts.modifiers != []: is_bold = 0 if 'b' in ts.modifiers: is_bold = 1 font_name += '-Bold' if 'o' in ts.modifiers: if not is_bold: font_name += '-' font_name += 'Oblique' elif 'i' in ts.modifiers: if not is_bold: font_name += '-' font_name += 'Italic' elif font_name in ('Palatino', 'Times', 'NewCenturySchlbk'): font_name += '-Roman' return (font_name, ts.size, ts.line_height, ts.color, ts.halign, ts.valign, ts.angle, str) def __parse_float(self): istart = self.i while self.i < len(self.str) and self.str[self.i] in string.digits or self.str[self.i] == '.': self.i += 1 return float(self.str[istart:self.i]) def __parse_int(self): istart = self.i while self.i < len(self.str) and \ (self.str[self.i] in string.digits or self.str[self.i] == '-'): self.i += 1 return int(self.str[istart:self.i]) def next(self): "Get the next text segment. Return an 8-element array: (FONTNAME, SIZE, LINEHEIGHT, COLOR, H_ALIGN, V_ALIGN, ANGLE, STR." l = [] changed = 0 self.old_state = self.ts.copy() while self.i < len(self.str): if self.str[self.i] == '/': self.i = self.i+1 ch = self.str[self.i] self.i = self.i+1 self.old_state = self.ts.copy() if ch == '/' or ch == '{' or ch == '}': l.append(ch) elif _font_family_map.has_key(ch): self.ts.family = _font_family_map[ch] changed = 1 elif ch == 'F': # /F{font-family} if self.str[self.i] != '{': raise FontException('"{" must follow /F', self.str) self.i += 1 istart = self.i while self.str[self.i] != '}': self.i += 1 if self.i >= len(self.str): raise FontException('Expecting "/F{...}"', self.str) self.ts.family = self.str[istart:self.i] self.i += 1 changed = 1 elif ch in string.digits: self.i -= 1 self.ts.size = self.__parse_int() self.ts.line_height = self.ts.size changed = 1 elif ch == 'l': self.ts.line_height = self.__parse_int() changed = 1 elif ch == 'b': self.ts.modifiers.append('b') changed = 1 elif ch == 'i': self.ts.modifiers.append('i') changed = 1 elif ch == 'o': self.ts.modifiers.append('q') changed = 1 elif ch == 'c': self.ts.color = color.gray_scale(self.__parse_float()) elif ch == 'v': if self.str[self.i] not in 'BTM': raise FontException('Undefined escape sequence "/v%c"' % self.str[self.i], self.str) self.ts.valign = self.str[self.i] self.i += 1 changed = 1 elif ch == 'h': if self.str[self.i] not in 'LRC': raise FontException('Undefined escape sequence "/h%c"' % self.str[self.i], self.str) self.ts.halign = self.str[self.i] self.i += 1 changed = 1 elif ch == 'a': self.ts.angle = self.__parse_int() changed = 1 else: raise FontException('Undefined escape sequence: "/%c"' % ch, self.str) elif self.str[self.i] == '{': self.stack.append(self.ts.copy()) self.i += 1 elif self.str[self.i] == '}': if len(self.stack) == 0: raise FontError('Unmatched "}"', self.str) self.ts = self.stack[-1] del self.stack[-1] self.i += 1 changed = 1 else: l.append(self.str[self.i]) self.i += 1 if changed and len(l) > 0: return self.__return_state(self.old_state, ''.join(l)) else: # font change in the beginning of the sequence doesn't count. self.old_state = self.ts.copy() changed = 0 if len(l) > 0: return self.__return_state(self.old_state, ''.join(l)) else: return None # # def unaligned_get_dimension(text): """Return the bounding box of the text, assuming that the left-bottom corner of the first letter of the text is at (0, 0). This procedure ignores /h, /v, and /a directives when calculating the BB; it just returns the alignment specifiers as a part of the return value. The return value is a tuple (width, height, halign, valign, angle).""" xmax = 0 ymax = 0 ymax = 0 angle = None halign = None valign = None itr = text_iterator(None) for line in unicode(text).split('\n'): cur_height = 0 cur_width = 0 itr.reset(line) while 1: elem = itr.next() if not elem: break (font, size, line_height, color, new_h, new_v, new_a, chunk) = elem if halign != None and new_h != halign: raise FontException('Only one "/h" can appear in a string.', unicode(text)) if valign != None and new_v != valign: raise FontException('Only one "/v" can appear in a string.', unicode(text)) if angle != None and new_a != angle: raise FontException('Only one "/a" can appear in a string.', unicode(text)) halign = new_h valign = new_v angle = new_a cur_width += line_width(font, size, chunk) cur_height = max(cur_height, line_height) xmax = max(cur_width, xmax) ymax += cur_height return (xmax, ymax, halign or theme.default_font_halign, valign or theme.default_font_valign, angle or theme.default_font_angle) def get_dimension(text): """Return the bounding box of the <text>, assuming that the left-bottom corner of the first letter of the text is at (0, 0). This procedure ignores /h, /v, and /a directives when calculating the boundingbox; it just returns the alignment specifiers as a part of the return value. The return value is a tuple (width, height, halign, valign, angle).""" (xmax, ymax, halign, valign, angle) = unaligned_get_dimension(text) xmin = ymin = 0 if halign == 'C': xmin = -xmax / 2.0 xmax = xmax / 2.0 elif halign == 'R': xmin = -xmax xmax = 0 if valign == 'M': ymin = -ymax / 2.0 ymax = ymax / 2.0 elif valign == 'T': ymin = -ymax ymax = 0 if angle != 0: (x0, y0) = pychart_util.rotate(xmin, ymin, angle) (x1, y1) = pychart_util.rotate(xmax, ymin, angle) (x2, y2) = pychart_util.rotate(xmin, ymax, angle) (x3, y3) = pychart_util.rotate(xmax, ymax, angle) xmax = max(x0, x1, x2, x3) xmin = min(x0, x1, x2, x3) ymax = max(y0, y1, y2, y3) ymin = min(y0, y1, y2, y3) return (xmin, xmax, ymin, ymax) return (xmin, xmax, ymin, ymax) def unaligned_text_width(text): x = unaligned_get_dimension(text) return x[0] def text_width(text): """Return the width of the <text> in points.""" (xmin, xmax, d1, d2) = get_dimension(text) return xmax-xmin def unaligned_text_height(text): x = unaligned_get_dimension(text) return x[1] def text_height(text): """Return the total height of the <text> and the length from the base point to the top of the text box.""" (d1, d2, ymin, ymax) = get_dimension(text) return (ymax-ymin, ymax) def get_align(text): "Return (halign, valign, angle) of the <text>." (x1, x2, h, v, a) = unaligned_get_dimension(text) return (h, v, a) def quotemeta(text): """Quote letters with special meanings in pychart so that <text> will display as-is when passed to canvas.show(). >>> font.quotemeta("foo/bar") "foo//bar" """ text = re.sub(r'/', '//', text) text = re.sub(r'\\{', '/{', text) text = re.sub(r'\\}', '/}', text) return text