diff --git a/mathics/builtin/graphics.py b/mathics/builtin/graphics.py index b48512075b..d93e6a173a 100644 --- a/mathics/builtin/graphics.py +++ b/mathics/builtin/graphics.py @@ -11,6 +11,7 @@ from math import floor, ceil, log10 import json +import base64 from six.moves import map from six.moves import range from six.moves import zip @@ -1453,14 +1454,19 @@ def boxes_to_xml(self, leaves, **options): w += 2 h += 2 - xml = ( - '%s') % ( - width, height, xmin, ymin, w, h, svg) - - xml = """%s""" % xml - return xml + svg_xml = ''' + + %s + + ''' % (' '.join('%f' % t for t in (xmin, ymin, w, h)), svg) + + return '' % ( + int(width), + int(height), + base64.b64encode(svg_xml.encode('utf8')).decode('utf8')) def axis_ticks(self, xmin, xmax): def round_to_zero(value): diff --git a/mathics/builtin/inout.py b/mathics/builtin/inout.py index debfecda2d..87e19c6c08 100644 --- a/mathics/builtin/inout.py +++ b/mathics/builtin/inout.py @@ -1732,8 +1732,7 @@ class MathMLForm(Builtin): = \u03bc #> MathMLForm[Graphics[Text["\u03bc"]]] - = \u03bc + = ## The should contain U+2062 INVISIBLE TIMES #> MathMLForm[MatrixForm[{{2*a, 0},{0,0}}]] diff --git a/mathics/core/evaluation.py b/mathics/core/evaluation.py index 818f0f41e7..fe56a650f0 100644 --- a/mathics/core/evaluation.py +++ b/mathics/core/evaluation.py @@ -144,9 +144,23 @@ def get_data(self): } +class Output(object): + def max_stored_size(self, settings): + return settings.MAX_STORED_SIZE + + def out(self, out): + pass + + def clear(self, wait): + raise NotImplementedError + + def display(self, data, metadata): + raise NotImplementedError + + class Evaluation(object): def __init__(self, definitions=None, - out_callback=None, format='text', catch_interrupt=True): + output=None, format='text', catch_interrupt=True): from mathics.core.definitions import Definitions if definitions is None: @@ -156,7 +170,7 @@ def __init__(self, definitions=None, self.timeout = False self.stopped = False self.out = [] - self.out_callback = out_callback + self.output = output if output else Output() self.listeners = {} self.options = None self.predetermined_out = None @@ -221,7 +235,7 @@ def evaluate(): self.definitions.add_rule('Out', Rule( Expression('Out', line_no), stored_result)) if result != Symbol('Null'): - return self.format_output(result) + return self.format_output(result, self.format) else: return None try: @@ -261,7 +275,7 @@ def evaluate(): if exc_result is not None: self.recursion_depth = 0 if exc_result != Symbol('Null'): - result = self.format_output(exc_result) + result = self.format_output(exc_result, self.format) result = Result(self.out, result, line_no) self.out = [] @@ -288,23 +302,31 @@ def get_stored_result(self, result): # Prevent too large results from being stored, as this can exceed the # DB's max_allowed_packet size - data = pickle.dumps(result) - if len(data) > 10000: - return Symbol('Null') + max_stored_size = self.output.max_stored_size(settings) + if max_stored_size is not None: + data = pickle.dumps(result) + if len(data) > max_stored_size: + return Symbol('Null') return result def stop(self): self.stopped = True - def format_output(self, expr): + def format_output(self, expr, format=None): + if format is None: + format = self.format + + if isinstance(format, dict): + return dict((k, self.format_output(expr, f)) for k, f in format.items()) + from mathics.core.expression import Expression, BoxError - if self.format == 'text': + if format == 'text': result = expr.format(self, 'System`OutputForm') - elif self.format == 'xml': + elif format == 'xml': result = Expression( 'StandardForm', expr).format(self, 'System`MathMLForm') - elif self.format == 'tex': + elif format == 'tex': result = Expression('StandardForm', expr).format( self, 'System`TeXForm') else: @@ -368,20 +390,18 @@ def message(self, symbol, tag, *args): text = String("Message %s::%s not found." % (symbol_shortname, tag)) text = self.format_output(Expression( - 'StringForm', text, *(from_python(arg) for arg in args))) + 'StringForm', text, *(from_python(arg) for arg in args)), 'text') self.out.append(Message(symbol_shortname, tag, text)) - if self.out_callback: - self.out_callback(self.out[-1]) + self.output.out(self.out[-1]) def print_out(self, text): from mathics.core.expression import from_python - text = self.format_output(from_python(text)) + text = self.format_output(from_python(text), 'text') self.out.append(Print(text)) - if self.out_callback: - self.out_callback(self.out[-1]) + self.output.out(self.out[-1]) if settings.DEBUG_PRINT: print('OUT: ' + text) diff --git a/mathics/main.py b/mathics/main.py index 42070a7d93..e21e4f735e 100644 --- a/mathics/main.py +++ b/mathics/main.py @@ -13,7 +13,7 @@ from mathics.core.definitions import Definitions from mathics.core.expression import strip_context -from mathics.core.evaluation import Evaluation +from mathics.core.evaluation import Evaluation, Output from mathics.core.parser import LineFeeder, FileLineFeeder from mathics import version_string, license_string, __version__ from mathics import settings @@ -176,6 +176,14 @@ def empty(self): return False +class TerminalOutput(Output): + def __init__(self, shell): + self.shell = shell + + def out(self, out): + return self.shell.out_callback(out) + + def main(): argparser = argparse.ArgumentParser( prog='mathics', @@ -238,7 +246,7 @@ def main(): if args.execute: for expr in args.execute: print(shell.get_in_prompt() + expr) - evaluation = Evaluation(shell.definitions, out_callback=shell.out_callback) + evaluation = Evaluation(shell.definitions, output=TerminalOutput(shell)) result = evaluation.parse_evaluate(expr, timeout=settings.TIMEOUT) shell.print_result(result) @@ -249,7 +257,8 @@ def main(): feeder = FileLineFeeder(args.FILE) try: while not feeder.empty(): - evaluation = Evaluation(shell.definitions, out_callback=shell.out_callback, catch_interrupt=False) + evaluation = Evaluation( + shell.definitions, output=TerminalOutput(shell), catch_interrupt=False) query = evaluation.parse_feeder(feeder) if query is None: continue @@ -270,7 +279,7 @@ def main(): while True: try: - evaluation = Evaluation(shell.definitions, out_callback=shell.out_callback) + evaluation = Evaluation(shell.definitions, output=TerminalOutput(shell)) query = evaluation.parse_feeder(shell) if query is None: continue diff --git a/mathics/settings.py b/mathics/settings.py index c7cc79300a..d49b0f4730 100644 --- a/mathics/settings.py +++ b/mathics/settings.py @@ -27,6 +27,10 @@ MAX_RECURSION_DEPTH = 512 +# max pickle.dumps() size for storing results in DB +# historically 10000 was used on public mathics servers +MAX_STORED_SIZE = 10000 + ADMINS = ( ('Admin', 'mail@test.com'), ) diff --git a/mathics/web/media/js/mathics.js b/mathics/web/media/js/mathics.js index 45e34f3227..02bf3bd374 100644 --- a/mathics/web/media/js/mathics.js +++ b/mathics/web/media/js/mathics.js @@ -274,10 +274,38 @@ function translateDOMElement(element, svg) { return dom; } +function convertMathGlyphs(dom) { + // convert mglyphs to their classic representation ( or ), so the new mglyph logic does not make + // anything worse in the classic Mathics frontend for now. In the long run, this code should vanish. + + var MML = "http://www.w3.org/1998/Math/MathML"; + var glyphs = dom.getElementsByTagName("mglyph"); + for (var i = 0; i < glyphs.length; i++) { + var glyph = glyphs[i]; + var src = glyph.getAttribute('src'); + if (src.startsWith('data:image/svg+xml;base64,')) { + var svgText = atob(src.substring(src.indexOf(",") + 1)); + var mtable =document.createElementNS(MML, "mtable"); + mtable.innerHTML = '' + svgText + ''; + var svg = mtable.getElementsByTagNameNS("*", "svg")[0]; + svg.setAttribute('width', glyph.getAttribute('width')); + svg.setAttribute('height', glyph.getAttribute('height')); + glyph.parentNode.replaceChild(mtable, glyph); + } else if (src.startsWith('data:image/')) { + var img = document.createElement('img'); + img.setAttribute('src', src) + img.setAttribute('width', glyph.getAttribute('width')); + img.setAttribute('height', glyph.getAttribute('height')); + glyph.parentNode.replaceChild(img, glyph); + } + } +} + function createLine(value) { if (value.startsWith('') results = [] try: