def parse(self, str): """Parse equation object string representation.""" str = str.rstrip("\r\n") l = str.split(';') if len(l) != 5: _logger.error(_('Equation.parse() string invalid (%s)'), str) return False if l[2].startswith("<svg>"): l[2] = SVGImage(data=base64.b64decode(l[2][5:])) # Should figure out how to use MathLib directly in a non-hacky way else: try: l[2] = Decimal(l[2]) except Exception, inst: pass
def parse(self, str): """Parse equation object string representation.""" str = str.rstrip("\r\n") k = str.split(';') if len(k) != 5: _logger.error(_('Equation.parse() string invalid (%s)'), str) return False if k[2].startswith("<svg>"): k[2] = SVGImage(data=base64.b64decode(k[2][5:])) # Should figure out how to use MathLib directly in a non-hacky way else: try: k[2] = Decimal(k[2]) except Exception: pass self.set(k[0], k[1], k[2], XoColor(color_string=k[3]), k[4])
def process(self): """Parse the equation entered and show the result.""" s = _s(self.text_entry.get_text()) label = self.label_entry.get_text() _logger.debug('process(): parsing %r, label: %r', s, label) try: tree = self.parser.parse(s) res = self.parser.evaluate(tree) except ParserError as e: res = e self.showing_error = True if isinstance(res, str) and res.find('</svg>') > -1: res = SVGImage(data=res) _logger.debug('Result: %r', res) # Check whether assigning this label would cause recursion if not isinstance(res, ParserError) and len(label) > 0: lastpos = self.parser.get_var_used_ofs(label) if lastpos is not None: res = RuntimeError( _('Can not assign label: will cause recursion'), lastpos) # If parsing went ok, see if we have to replace the previous answer # to get a (more) exact result if self.ans_inserted and not isinstance(res, ParserError) \ and not isinstance(res, SVGImage): ansvar = self.format_insert_ans() pos = s.find(ansvar) if len(ansvar) > 6 and pos != -1: s2 = s.replace(ansvar, 'LastEqn') _logger.debug( 'process(): replacing previous answer %r: %r', ansvar, s2) tree = self.parser.parse(s2) res = self.parser.evaluate(tree) if isinstance(res, ParserError): eqn = Equation(label, _n(s), res, self.color, self.get_owner_id(), ml=self.ml) self.set_error_equation(eqn) else: eqn = Equation(label, _n(s), _n(str(res)), self.color, self.get_owner_id(), ml=self.ml) self.add_equation(eqn, drawlasteq=True, tree=tree) self.send_message("add_eq", value=str(eqn)) self.parser.set_var('Ans', eqn.result) # Setting LastEqn to the parse tree would certainly be faster, # however, it introduces recursion problems self.parser.set_var('LastEqn', eqn.result) self.showing_error = False self.ans_inserted = False self.text_entry.set_text('') self.label_entry.set_text('') return res is not None
def size_for_node(self, node, client): '''Given a docutils image node, returns the size the image should have in the PDF document, and what 'kind' of size that is. That involves lots of guesswork''' uri = os.path.join(client.basedir, str(node.get("uri"))) srcinfo = client, uri # Extract all the information from the URI imgname, extension, options = self.split_uri(uri) if not os.path.isfile(imgname): imgname = missing scale = float(node.get('scale', 100)) / 100 size_known = False # Figuring out the size to display of an image is ... annoying. # If the user provides a size with a unit, it's simple, adjustUnits # will return it in points and we're done. # However, often the unit wil be "%" (specially if it's meant for # HTML originally. In which case, we will use a percentage of # the containing frame. # Find the image size in pixels: kind = 'direct' xdpi, ydpi = client.styles.def_dpi, client.styles.def_dpi extension = imgname.split('.')[-1].lower() if extension in ['svg', 'svgz'] and SVGImage.available(): iw, ih = SVGImage(imgname, srcinfo=srcinfo).wrap(0, 0) # These are in pt, so convert to px iw = iw * xdpi / 72 ih = ih * ydpi / 72 elif extension in [ "ai", "ccx", "cdr", "cgm", "cmx", "sk1", "sk", "xml", "wmf", "fig" ] and VectorImage.available(): iw, ih = VectorImage(imgname, srcinfo=srcinfo).wrap(0, 0) # These are in pt, so convert to px iw = iw * xdpi / 72 ih = ih * ydpi / 72 elif extension == 'pdf': if VectorPdf is not None: box = VectorPdf.load_xobj(srcinfo).BBox else: pdf = LazyImports.pdfinfo if pdf is None: log.warning( 'PDF images are not supported without pyPdf or pdfrw [%s]', nodeid(node)) return 0, 0, 'direct' reader = pdf.PdfFileReader(open(imgname, 'rb')) box = [float(x) for x in reader.getPage(0)['/MediaBox']] x1, y1, x2, y2 = box # These are in pt, so convert to px iw = float((x2 - x1) * xdpi / 72) ih = float((y2 - y1) * ydpi / 72) size_known = True # Assume size from original PDF is OK else: keeptrying = True if LazyImports.PILImage: try: img = LazyImports.PILImage.open(imgname) img.load() iw, ih = img.size xdpi, ydpi = img.info.get('dpi', (xdpi, ydpi)) keeptrying = False except IOError: # PIL throws this when it's a broken/unknown image pass if keeptrying and LazyImports.PMImage: img = LazyImports.PMImage(imgname) iw = img.size().width() ih = img.size().height() density = img.density() # The density is in pixelspercentimeter (!?) xdpi = density.width() * 2.54 ydpi = density.height() * 2.54 keeptrying = False if keeptrying: log.error( "The image (%s, %s) is broken or in an unknown format", imgname, nodeid(node)) raise ValueError # Try to get the print resolution from the image itself via PIL. # If it fails, assume a DPI of 300, which is pretty much made up, # and then a 100% size would be iw*inch/300, so we pass # that as the second parameter to adjustUnits # # Some say the default DPI should be 72. That would mean # the largest printable image in A4 paper would be something # like 480x640. That would be awful. # w = node.get('width') h = node.get('height') if h is None and w is None: # Nothing specified # Guess from iw, ih log.warning( "Using image %s without specifying size." "Calculating based on image size at %ddpi [%s]", imgname, xdpi, nodeid(node)) w = iw * inch / xdpi h = ih * inch / ydpi elif w is not None: # Node specifies only w # In this particular case, we want the default unit # to be pixels so we work like rst2html if w[-1] == '%': kind = 'percentage_of_container' w = int(w[:-1]) else: # This uses default DPI setting because we # are not using the image's "natural size" # this is what LaTeX does, according to the # docutils mailing list discussion w = client.styles.adjustUnits(w, client.styles.tw, default_unit='px') if h is None: # h is set from w with right aspect ratio h = w * ih / iw else: h = client.styles.adjustUnits(h, ih * inch / ydpi, default_unit='px') elif h is not None and w is None: if h[-1] != '%': h = client.styles.adjustUnits(h, ih * inch / ydpi, default_unit='px') # w is set from h with right aspect ratio w = h * iw / ih else: log.error('Setting height as a percentage does **not** work. '\ 'ignoring height parameter [%s]', nodeid(node)) # Set both from image data w = iw * inch / xdpi h = ih * inch / ydpi # Apply scale factor w = w * scale h = h * scale # And now we have this probably completely bogus size! log.info("Image %s size calculated: %fcm by %fcm [%s]", imgname, w / cm, h / cm, nodeid(node)) return w, h, kind
def process(self): """Parse the equation entered and show the result""" s = unicode(self.text_entry.get_text()) label = unicode(self.label_entry.get_text()) _logger.debug('process(): parsing %r, label: %r', s, label) try: tree = self.parser.parse(s) res = self.parser.evaluate(tree) except ParserError, e: res = e self.showing_error = True if isinstance(res, str) and res.find('</svg>') > -1: res = SVGImage(data=res) _logger.debug('Result: %r', res) # Check whether assigning this label would cause recursion if not isinstance(res, ParserError) and len(label) > 0: lastpos = self.parser.get_var_used_ofs(label) if lastpos is not None: res = RuntimeError( _('Can not assign label: will cause recursion'), lastpos) # If parsing went ok, see if we have to replace the previous answer # to get a (more) exact result if self.ans_inserted and not isinstance(res, ParserError) \ and not isinstance(res, SVGImage): ansvar = self.format_insert_ans()
class Calculate(ShareableActivity): TYPE_FUNCTION = 1 TYPE_OP_PRE = 2 TYPE_OP_POST = 3 TYPE_TEXT = 4 SELECT_NONE = 0 SELECT_SELECT = 1 SELECT_TAB = 2 KEYMAP = { 'Return': lambda o: o.process(), 'period': '.', 'equal': '=', 'plus': '+', 'minus': '-', 'asterisk': '*', 'multiply': '×', 'divide': '÷', 'slash': '/', 'BackSpace': lambda o: o.remove_character(-1), 'Delete': lambda o: o.remove_character(1), 'parenleft': '(', 'parenright': ')', 'exclam': '!', 'ampersand': '&', 'bar': '|', 'asciicircum': '^', 'less': '<', 'greater': '>', 'percent': '%', 'comma': ',', 'underscore': '_', 'Left': lambda o: o.move_left(), 'Right': lambda o: o.move_right(), 'Up': lambda o: o.get_older(), 'Down': lambda o: o.get_newer(), 'colon': lambda o: o.label_entered(), 'Home': lambda o: o.text_entry.set_position(0), 'End': lambda o: o.text_entry.set_position(len(o.text_entry.get_text())), 'Tab': lambda o: o.tab_complete(), } CTRL_KEYMAP = { 'c': lambda o: o.text_copy(), 'v': lambda o: o.text_paste(), 'x': lambda o: o.text_cut(), 'q': lambda o: o.close(), 'a': lambda o: o.text_select_all(), } SHIFT_KEYMAP = { 'Left': lambda o: o.expand_selection(-1), 'Right': lambda o: o.expand_selection(1), 'Home': lambda o: o.expand_selection(-1000), 'End': lambda o: o.expand_selection(1000), } IDENTIFIER_CHARS = \ u"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_ " def __init__(self, handle): ShareableActivity.__init__(self, handle) self.old_eqs = [] self.ml = MathLib() self.parser = AstParser(self.ml) # These will result in 'Ans <operator character>' being inserted self._chars_ans_diadic = [ op[0] for op in self.parser.get_diadic_operators() ] try: self._chars_ans_diadic.remove('-') except: pass self.KEYMAP['multiply'] = self.ml.mul_sym self.KEYMAP['divide'] = self.ml.div_sym self.KEYMAP['equal'] = self.ml.equ_sym self.clipboard = gtk.Clipboard() self.select_reason = self.SELECT_SELECT self.buffer = u"" self.showing_version = 0 self.showing_error = False self.ans_inserted = False self.show_vars = False self.connect("key_press_event", self.keypress_cb) self.connect("destroy", self.cleanup_cb) self.color = sugar.profile.get_color() self.layout = CalcLayout(self) self.label_entry = self.layout.label_entry self.text_entry = self.layout.text_entry self.last_eq_sig = None self.last_eqn_textview = None self.reset() self.layout.show_it() self.connect('joined', self._joined_cb) self.parser.log_debug_info() def ignore_key_cb(self, widget, event): return True def cleanup_cb(self, arg): _logger.debug('Cleaning up...') def equation_pressed_cb(self, eqn): """Callback for when an equation box is clicked""" if isinstance(eqn.result, SVGImage): return True if len(eqn.label) > 0: text = eqn.label else: # don't insert plain text if type(eqn.result) in (types.StringType, types.UnicodeType): text = '' else: text = self.parser.ml.format_number(eqn.result) self.button_pressed(self.TYPE_TEXT, text) return True def set_last_equation(self, eqn): """Set the 'last equation' TextView""" if self.last_eq_sig is not None: self.layout.last_eq.disconnect(self.last_eq_sig) self.last_eq_sig = None if not isinstance(eqn.result, ParserError): self.last_eq_sig = self.layout.last_eq.connect( 'button-press-event', lambda a1, a2, e: self.equation_pressed_cb(e), eqn) self.layout.last_eq.set_buffer(eqn.create_lasteq_textbuf()) def set_error_equation(self, eqn): """Set equation with error markers. Since set_last_equation implements this we can just forward the call.""" self.set_last_equation(eqn) def clear_equations(self): """Clear the list of old equations.""" self.old_eqs = [] self.showing_version = 0 def add_equation(self, eq, prepend=False, drawlasteq=False, tree=None): """ Insert equation in the history list and set variable if assignment. Input: eq: the equation object prepend: if True, prepend to list, else append drawlasteq: if True, draw in 'last equation' textbox and queue the buffer to be added to the history next time an equation is added. tree: the parsed tree, this will be used to set the label variable so that the equation can be used symbolicaly. """ if eq.equation is not None and len(eq.equation) > 0: if prepend: self.old_eqs.insert(0, eq) else: self.old_eqs.append(eq) self.showing_version = len(self.old_eqs) if self.last_eqn_textview is not None and drawlasteq: # Prepending here should be the opposite: prepend -> eqn on top. # We always own this equation self.layout.add_equation(self.last_eqn_textview, True, prepend=not prepend) self.last_eqn_textview = None own = (eq.owner == self.get_owner_id()) w = eq.create_history_object() w.connect('button-press-event', lambda w, e: self.equation_pressed_cb(eq)) if drawlasteq: self.set_last_equation(eq) # SVG images can't be plotted in last equation window if isinstance(eq.result, SVGImage): self.layout.add_equation(w, own, prepend=not prepend) else: self.last_eqn_textview = w else: self.layout.add_equation(w, own, prepend=not prepend) if eq.label is not None and len(eq.label) > 0: w = self.create_var_textview(eq.label, eq.result) if w is not None: self.layout.add_variable(eq.label, w) if tree is None: tree = self.parser.parse(eq.equation) self.parser.set_var(eq.label, tree) # FIXME: to be implemented def process_async(self, eqn): """Parse and process an equation asynchronously.""" def process(self): """Parse the equation entered and show the result""" s = unicode(self.text_entry.get_text()) label = unicode(self.label_entry.get_text()) _logger.debug('process(): parsing %r, label: %r', s, label) try: tree = self.parser.parse(s) res = self.parser.evaluate(tree) except ParserError, e: res = e self.showing_error = True if isinstance(res, str) and res.find('</svg>') > -1: res = SVGImage(data=res) _logger.debug('Result: %r', res) # Check whether assigning this label would cause recursion if not isinstance(res, ParserError) and len(label) > 0: lastpos = self.parser.get_var_used_ofs(label) if lastpos is not None: res = RuntimeError( _('Can not assign label: will cause recursion'), lastpos) # If parsing went ok, see if we have to replace the previous answer # to get a (more) exact result if self.ans_inserted and not isinstance(res, ParserError) \ and not isinstance(res, SVGImage): ansvar = self.format_insert_ans() pos = s.find(ansvar) if len(ansvar) > 6 and pos != -1: s2 = s.replace(ansvar, 'LastEqn') _logger.debug('process(): replacing previous answer %r: %r', ansvar, s2) tree = self.parser.parse(s2) res = self.parser.evaluate(tree) eqn = Equation(label, s, res, self.color, self.get_owner_id(), ml=self.ml) if isinstance(res, ParserError): self.set_error_equation(eqn) else: self.add_equation(eqn, drawlasteq=True, tree=tree) self.send_message("add_eq", value=str(eqn)) self.parser.set_var('Ans', eqn.result) # Setting LastEqn to the parse tree would certainly be faster, # however, it introduces recursion problems self.parser.set_var('LastEqn', eqn.result) self.showing_error = False self.ans_inserted = False self.text_entry.set_text(u'') self.label_entry.set_text(u'') return res is not None