Пример #1
0
def render_video(videob64, args):
    """
    Render video (webm) encoded in base64 in svg command
    """


    #Get video size to get the ratio (to estimage the height)
    vidw, vidh = get_video_size(args)
    scale_x = float(convert_unit(args['width']))/float(vidw)
    width = convert_unit(args['width'])
    height = vidh * scale_x 

    if document._output_format=='html5':
        #HTML tag for video 
        output = """<video id='video' width="%spx" %s controls="controls" type="video/%s" src="data:video/%s;base64, %s"/>"""

        #Check if we need autoplay
        otherargs = ''    
        if args['autoplay'] == True:
            otherargs += ' autoplay'

        output = output%(width, otherargs, args['ext'], args['ext'], videob64)

    else:
        #Get video image 
        _, imgframe = video_first_image(args)
        imgframe = base64.b64encode( imgframe )
        output = '<image x="0" y="0" width="%s" height="%s" xlink:href="data:image/jpg;base64, %s" />'%(str(width), str(height), imgframe)
        
    return output, float(width), float(height)
Пример #2
0
    def __init__(self, x2, y2, **kwargs):
        """
            Create an svg line
            
            # Arguments:
            
            -x["center"]: horizontal position in the slide
            -y["auto"]: vertical position in the slide
            -x2: the horizontal position of the end point
            -y2: the vertical position of the end point
            
            -color: the svg colorname to fill your rectangle
            -linewidth: the with of the stroke line
            -opacity: the value of the rectangle opacity from 0 to 1
            
            # exemple:
            
            line(x=0, y="20px", x2="40px", y2="20px", color="crimson")
        """

        #The input type of the module
        self.type = 'svg'

        #Add args to the module
        self.check_args_from_theme(kwargs)
        self.x2 = x2
        self.y2 = y2
        self.args['x2'] = self.x2
        self.args['y2'] = self.y2

        #convert unit of x2 and y2
        self.x2 = convert_unit(self.x2)
        self.y2 = convert_unit(self.y2)

        #Build style for the rectangle
        beampy_svg_kword = {
            'color': 'stroke',
            'linewidth': 'stroke-width',
            'opacity': 'opacity'
        }

        style = ''
        for kw in beampy_svg_kword:
            if hasattr(self, kw):
                style += '%s:%s;' % (beampy_svg_kword[kw], getattr(self, kw))

        self.content = '<line x1="0" y1="0" x2="{x2}px" y2="{y2}px" style="{style}"/>'.format(
            x2=self.x2, y2=self.y2, style=style)

        #Register the module
        self.register()
Пример #3
0
def itemize(items_list, **kwargs):
    '''

	Generates a list or an enumeration.

	See THEME['itemize'] for option

	TODO: ADD doc for function arguments
	'''

    args = check_function_args(itemize, kwargs)
    number = 1

    if args['width'] != None:
        in_width = float(convert_unit(args['width'])) - float(
            convert_unit(args['item_indent']))
    else:
        in_width = float(document._width) - float(
            convert_unit(args['item_indent']))

    with group(width=args['width'], x=args['x'], y=args['y']):

        for i, the_item in enumerate(items_list):

            if args['item_style'] == 'bullet':
                item_char = r'$\bullet$'

            elif args['item_style'] == 'number':
                item_char = str(number) + r'.'
                number += 1

            else:
                item_char = args['item_style']

            # Add color

            item_char = color_text(item_char, args['item_color'])
            the_item = color_text(the_item, args['text_color'])

            if i == 0:
                text(item_char + r' ' + the_item,
                     x=args['item_indent'],
                     y=0,
                     width=in_width)
            else:
                text(item_char + r' ' + the_item,
                     x=args['item_indent'],
                     y=args['item_spacing'],
                     width=in_width)
Пример #4
0
    def process_value(self):
        if self.elem_id is not None and self.slide_id is not None:
            if self.value is None:
                self.run_render()

        if isinstance(self.value, str):
            self.value = float(convert_unit(self.value))
Пример #5
0
    def __init__(self, title=None, **kwargs):

        # Add a slide to the global counter
        if 'slide' in document._global_counter:
            document._global_counter['slide'] += 1
        else:
            document._global_counter['slide'] = 0
        # Init group counter
        document._global_counter['group'] = 0

        # check args from THEME
        self.args = check_function_args(slide, kwargs)

        # The id for this slide
        self.id = gcs()
        self.slide_num = document._global_counter['slide']

        # Change from dict to class
        self.tmpout = ''
        self.contents = {}
        self.element_keys = []
        self.cpt_anim = 0
        self.num = document._global_counter['slide']+1
        self.title = title
        self.curwidth = document._width
        self.num_layers = 0 # Store the number of layers in this slide

        # Store all outputs
        self.svgout = []
        self.svgdefout = []  # Store module definition for slide
        self.htmlout = {}  # Html is a dict, each key dict is a layer htmlout[0] = [html, html, html] etc...
        self.scriptout = []
        self.animout = []
        self.svgheader = ''
        self.svgfooter = '\n</svg>\n'
        self.svglayers = {}  # Store slide final svg (without svg defs stored in self.svgdefout) for the given layer

        # Do we need to render the THEME layout on this slide
        self.render_layout = True

        # Add the slide to the document contents list
        document._slides[self.id] = self

        # Manage groups inside one slide the lower level of group is 0 and correspond
        # to the main slide
        self.groupsid = {}
        self.cur_group_level = -1
        g0 = group(x=0, y=0, width=document._width, height=document._height)
        self.groupsid[0] = [g0.id]  # store groups id objects in this list

        if title is not None:
            from beampy.modules.title import title as bptitle
            self.title_element = bptitle(title)
            self.ytop = float(convert_unit(self.title.reserved_y))
        else:
            self.ytop = 0
            self.title_element = None

        # Add ytop to the slide main group
        g0.yoffset = self.ytop
Пример #6
0
    def update_size(self, width, height):
        """
            Update width and height to computed ones by the element render.

            If width is less than 1px, use it as a percentage of the width
        """

        # Check if it's Length object
        if isinstance(width, Length):
            if width.value is None:
                width.run_render()

            width = width.value

        if isinstance(height, Length):
            if height.value is None:
                height.run_render()

            height = height.value

        # Convert width height if they are given in % of the current width
        if isinstance(width, str):
            if '%' in width:
                ratio = float(width.replace('%', '')) / 100.0
                width = document._slides[self.slideid].curwidth * ratio
            else:
                width = float(convert_unit(width))

        if isinstance(height, str):
            if '%' in height:
                ratio = float(height.replace('%', '')) / 100.0
                height = document._slides[self.slideid].curheight * ratio
            else:
                height = float(convert_unit(height))

        # Convert string to float
        # Convert width height to % if they are given as a float number less than one
        if width is not None and width < 1:
            ratio = width / 100.0
            width = document._slides[self.slideid].curwidth * ratio

        if height is not None and height < 1:
            ratio = height / 100.0
            height = document._slides[self.slideid].curheight * ratio

        self.width.value = width
        self.height.value = height
Пример #7
0
    def __init__(self, title= None, **kwargs):

        #Add a slide to the global counter
        if 'slide' in document._global_counter:
            document._global_counter['slide'] += 1
        else:
            document._global_counter['slide'] = 0
        #Init group counter
        document._global_counter['group'] = 0

        #check args from THEME
        self.args = check_function_args(slide, kwargs)

        out = {'title':title,
               'contents': {},
               'num':document._global_counter['slide']+1,
               'groups': [],
               "args": self.args,
               'htmlout': '', #store rendered htmlelements inside the slide
               'animout': [], #store svg rendered part of animatesvg
               'scriptout': '', #store javascript defined in element['script']
               'cpt_anim': 0,
               'element_keys': [] #list to store elements id in order
               }

        #The id for this slide
        self.id = gcs()


        #Change from dict to class
        self.tmpout = out
        self.contents = out['contents']
        self.element_keys = out['element_keys']
        self.cpt_anim = 0
        self.num = out['num']
        self.title = title

        #Store all outputs
        self.svgout = []
        self.htmlout = []
        self.scriptout = []
        self.animout = []
        self.svgheader = ''
        self.svgfooter = '\n</svg>\n'

        self.cpt_anim = 0
        self.groups = []

        #Add the slide to the document contents list
        document._contents[self.id] = out
        document._slides[self.id] = self

        if title!= None:
            from beampy.modules.title import title as bptitle
            bptitle( title )
            self.ytop = float(convert_unit(self.title.reserved_y))
        else:
            self.ytop = 0
Пример #8
0
def hline(y, **kwargs):
    """
        Create an horizontal line at y position
    """

    y = convert_unit(y)
    y = '%spx' % y

    return line(x=0, y=y, x2='%spx' % document._width, y2=y, **kwargs)
Пример #9
0
def center(shift=0):

    out = {'anchor': 'middle', 'shift': shift}

    if isinstance(shift, str):
        out['shift'] = float(convert_unit(shift))
        out['unit'] = 'px'

    return out
Пример #10
0
def right(shift=0):

    out = {'anchor': 'right', 'shift': shift}

    if isinstance(shift, str):
        out['shift'] = float(convert_unit(shift))
        out['unit'] = 'px'

    return out
Пример #11
0
def itemize( items_list, **kwargs):

	'''

	Generates a list or an enumeration.

	See THEME['itemize'] for option

	TODO: ADD doc for function arguments
	'''

	args = check_function_args(itemize, kwargs)
	number = 1

	if args['width']!=None:
		in_width = float(convert_unit(args['width'])) - float(convert_unit(args['item_indent']))
	else:
		in_width = float(document._width) - float(convert_unit(args['item_indent']))

	with group(width=args['width'], x=args['x'], y=args['y']):

		for i, the_item in enumerate(items_list) :

			if args['item_style'] == 'bullet' :
				item_char = r'$\bullet$'

			elif args['item_style'] == 'number' :
				item_char = str(number) + r'.'
				number += 1

			else :
				item_char = item_style

			# Add color

			item_char = color_text( item_char, args['item_color'] )
			the_item = color_text( the_item, args['text_color'] )

			if i == 0 :
				text( item_char + r' ' + the_item, x = args['item_indent'],
				y = 0, width=in_width )
			else:
				text( item_char + r' ' + the_item, x = args['item_indent'],
				y = args['item_spacing'], width=in_width )
Пример #12
0
    def __init__(self, files_in, **kwargs):

        # Add type
        self.type = 'animatesvg'

        # Check input args for this module
        self.check_args_from_theme(kwargs)

        # Cache is useless because we call figure function which handle the cache for each figures
        self.cache = False

        slide = document._slides[gcs()]

        # Add +1 to counter
        self.anim_num = slide.cpt_anim
        slide.cpt_anim += 1

        input_width = self.width  # Save the input width for mpl figures
        if self.width is None:
            self.width = slide.curwidth

        # Read all files from a given wildcard
        if isinstance(files_in, str):
            svg_files = glob.glob(files_in)

            # Need to sort using the first digits finded in the name
            svg_files = sorted(
                svg_files, key=lambda x: int(''.join(re.findall(r'\d+', x))))

        # If the input is a list of names or mpl figures or other compatible with figure
        elif isinstance(files_in, list):
            svg_files = files_in

            if input_width is None:
                width_inch, height_inch = files_in[0].get_size_inches()
                self.width = convert_unit("%fin" % (width_inch))
        else:
            print('Unknown input type for files_folder')
            sys.exit(0)

        # check how many images we wants
        if self.end == 'end':
            self.end = len(svg_files)

        # Add content
        self.content = svg_files[self.start:self.end]

        # Register the module
        self.register()
Пример #13
0
    def process_right_value(right_value):
        """
        Process the right value of the operation (could be another element Length or string with length + unit or pixels)
        """

        if isinstance(right_value, Length):
            if right_value.value is None:
                right_value.run_render()

            tmp_rvalue = float(right_value.value)

        if isinstance(right_value, str):
            tmp_rvalue = float(convert_unit(right_value))

        if isinstance(right_value, float) or isinstance(right_value, int):
            tmp_rvalue = float(right_value)

        return tmp_rvalue
Пример #14
0
    def parse_newvalue(self, new_value):
        """
            New_value can be a string like "+5cm" or a float 0.4 or a new dict
            like {"shift": 0, "align": 'left'}
        """
        #print(type(new_value))
        if type(new_value) == type(str()):
            self.position['shift'] = float( convert_unit(new_value) )
            self.position['unit'] = 'px'

        elif type(new_value) == type(float()) or type(new_value) == type(int()):
            self.position['shift'] = new_value

        elif type(new_value) == type(dict()):
            self.position = dict_deep_update(new_value.copy(), self.position)

        else:
            print('Invalid relative coordinate type!')
Пример #15
0
    def parse_newvalue(self, new_value):
        """
            New_value can be a string like "+5cm" or a float 0.4 or a new dict
            like {"shift": 0, "align": 'left'}
        """
        #print(type(new_value))
        if isinstance(new_value, str):
            self.position['shift'] = float(convert_unit(new_value))
            self.position['unit'] = 'px'

        elif isinstance(new_value, float) or isinstance(new_value, int):
            self.position['shift'] = new_value

        elif isinstance(new_value, dict):
            self.position = dict_deep_update(new_value.copy(), self.position)

        elif isinstance(new_value, Length):
            new_value.process_value()
            self.position['shift'] = float(new_value.value)
            self.position['unit'] = 'px'

        else:
            print('Invalid relative coordinate type!')
Пример #16
0
def render_figure( figurein, args ):
    
    """
        function to render figures
    """

    #For svg figure
    
    if args['ext'] in ('svg', 'pdf') :

        #test if we need to optimise the svg
        
        if document._optimize_svg:
            figurein = optimize_svg(figurein)

        soup = BeautifulSoup(figurein, 'xml')

        #Change id in svg defs to use the global id system
        soup = make_global_svg_defs(soup)

        svgtag = soup.find('svg')

        svg_viewbox = svgtag.get("viewBox")

        tmph = svgtag.get("height")
        tmpw = svgtag.get("width")
        if tmph == None or tmpw == None:
            fmpf, tmpname = tempfile.mkstemp(prefix="beampytmp")
            with open( tmpname+'.svg', 'w' ) as f:
                f.write(figurein)
                #print figurein
            tmph = getsvgheight( tmpname+'.svg' )
            tmpw = getsvgwidth( tmpname+'.svg' )
            #print tmpw, tmph
            os.remove(tmpname+'.svg')

        
        svgheight = convert_unit( tmph )
        svgwidth = convert_unit( tmpw )

        if svg_viewbox != None:
            svgheight = svg_viewbox.split(' ')[3]
            svgwidth = svg_viewbox.split(' ')[2]

        #SCALE OK need to keep the original viewBox !!!
        scale_x = float(convert_unit(args['width']))/float(svgwidth)
        #print svgwidth, svgheight, scale_x
        #scale_y = float(convert_unit(args['height']))/float(svgheight)
        good_scale = scale_x

        #BS4 get the svg tag content without <svg> and </svg>
        tmpfig = svgtag.renderContents()

        #print tmpfig[:100]

        #Add the correct first line and last
        #tmphead = '<g  transform="matrix(%s,0,0,%s,%s,%s)" viewBox="%s">'%(str(good_scale), str(good_scale), convert_unit(args['x']), convert_unit(args['y']), svg_viewbox))
        tmphead = '\n<g transform="scale(%0.5f)">'%(good_scale)
        output = tmphead + tmpfig + '</g>\n'

        figure_height = float(svgheight)*good_scale
        figure_width = convert_unit(args['width'])
        
    #Bokeh images
    if args['ext'] == 'bokeh':
        figure_height = float(convert_unit(args['height']))
        figure_width = convert_unit(args['width'])
        output = """%s"""%figurein


    #For the other format
    if args['ext'] in ['png', 'jpeg']:
        #Open image with PIL to compute size
        tmp_img = Image.open(args['filename'])
        _,_,tmpwidth,tmpheight = tmp_img.getbbox()
        tmp_img.close()
        scale_x = float(convert_unit(args['width']))/float(tmpwidth)
        figure_height = float(tmpheight) * scale_x
        figure_width = convert_unit(args['width'])
        
    if args['ext'] == 'png':
        output = '<image x="0" y="0" width="%s" height="%s" xlink:href="data:image/png;base64, %s" />'%(figure_width, figure_height, figurein)

    if args['ext'] == 'jpeg':
        output = '<image x="0" y="0" width="%s" height="%s" xlink:href="data:image/jpg;base64, %s" />'%(figure_width, figure_height, figurein)
    
    #Save the heigt
    #args['height'] = figure_height
    
    return output, float(figure_width), float(figure_height)
Пример #17
0
    def convert_position(self):

        # Function to convert position of an element
        tmpx = DEFAULT_X.copy()
        tmpy = DEFAULT_Y.copy()
        slidects = document._slides[self.slideid].contents

        # Get the previous content if it exist (to us "+xx" or "-yy" in x, y coords)
        if self.id_index > 0:
            prev_ct = slidects[document._slides[self.slideid].element_keys[
                self.id_index - 1]]
        else:
            prev_ct = None

        # Check if x or y are only floats
        if isinstance(self.x, float) or isinstance(self.x, int):
            tmpx['shift'] = self.x

        elif isinstance(self.x, dict):
            tmpx = dict_deep_update(tmpx, self.x)

        elif isinstance(self.x, str):

            converted = False

            if '+' in self.x:
                newx = convert_unit(self.x.replace('+', ''))
                # Make relative placement
                if prev_ct is not None:
                    dict_old = prev_ct.positionner.right + newx
                    tmpx = dict_deep_update(tmpx, dict_old)
                else:
                    tmpx['shift'] = newx

                tmpx['unit'] = 'px'
                converted = True

            if '-' in self.x:
                newx = convert_unit(self.x.replace('-', ''))
                # Make relative placement
                if prev_ct is not None:
                    dict_old = prev_ct.positionner.left - newx
                    tmpx = dict_deep_update(tmpx, dict_old)
                else:
                    tmpx['shift'] = newx
                tmpx['unit'] = 'px'
                converted = True

            if self.x in ['auto', 'center']:
                tmpx['shift'] = 0
                tmpx['align'] = self.x
                converted = True

            if not converted:
                try:
                    tmpx['shift'] = float(convert_unit(self.x))
                    tmpx['unit'] = 'px'
                except:
                    print('[Error] x position is incorect string format')
                    print(self.x)

        elif isinstance(self.x, Length):
            if self.x.value is None:
                self.x.run_render()

            tmpx['shift'] = self.x.value
            tmpx['unit'] = 'px'

        else:
            print("[Error] x position need to be a float or a dict")

        if isinstance(self.y, float) or isinstance(self.y, int):
            tmpy['shift'] = self.y

        elif isinstance(self.y, dict):
            tmpy = dict_deep_update(tmpy, self.y)

        elif isinstance(self.y, str):

            converted = False
            if '+' in self.y:
                newy = convert_unit(self.y.replace('+', ''))
                # Make relative placement
                if prev_ct is not None:
                    dict_old = prev_ct.positionner.bottom + newy
                    tmpy = dict_deep_update(tmpy, dict_old)
                else:
                    tmpy['shift'] = newy

                tmpy['unit'] = 'px'
                converted = True

            if '-' in self.y:
                newy = convert_unit(self.y.replace('-', ''))
                # Make relative placement
                if prev_ct is not None:
                    dict_old = prev_ct.positionner.top - newy
                    tmpy = dict_deep_update(tmpy, dict_old)
                else:
                    tmpy['shift'] = newy
                tmpy['unit'] = 'px'
                converted = True

            if self.y in ['auto', 'center']:
                tmpy['shift'] = 0
                tmpy['align'] = self.y
                converted = True

            if not converted:
                try:
                    tmpy['shift'] = convert_unit(self.y)
                    tmpy['unit'] = 'px'
                except:
                    print('[Error] y position is incorect string format')
                    print(self.y)

        elif isinstance(self.y, Length):
            if self.y.value is None:
                self.y.run_render()

            tmpy['shift'] = self.y.value
            tmpy['unit'] = 'px'

        else:
            print("[Error] y position need to be a float or an int or a dict")

        # Store the dict for positions
        self.x = tmpx
        self.y = tmpy

        # Force unit to be pixel for x > 1
        if self.x['shift'] > 1.0 and self.x['unit'] in ('width', 'height'):
            self.x['unit'] = 'px'

        # Force unit to be pixel for y > 1
        if self.y['shift'] > 1.0 and self.y['unit'] in ('width', 'height'):
            self.y['unit'] = 'px'

        # Convert position unit to px
        if self.x['unit'] in ['cm', 'pt', 'mm']:
            self.x['shift'] = float(
                convert_unit('%f%s' % (self.x['shift'], self.x['unit'])))

        if self.y['unit'] in ['cm', 'pt', 'mm']:
            self.y['shift'] = float(
                convert_unit('%f%s' % (self.y['shift'], self.y['unit'])))
Пример #18
0
def arrow(x,
          y,
          dx,
          dy,
          style='->',
          color="black",
          lw='2pt',
          in_angle=None,
          out_angle=None,
          bend=None,
          head_style=""):
    """
    function to draw arrows on slide

    Arguments
    ---------

    x: initial x position of the arrow
    y: initial y position

    dx: end of arrow relative to x
    dy: end of arrow relative to y

    style ["->"]: arrow style "-": a simple line
                              "->": simple arrow
                              "<->": two way arrows

    color ['black']: arrow color (you can use svgnames)

    lw ["2pt"]: set the line width in point

    in_angle  [None]: angle at the end of the arrow
    out_angle [None]: angle at the start of the arrow
    bend [None]: direction of arrow bending "left" or "right"

    head_style [""]: change head_style of arrow: "latex", "stealth"
    """

    tikz_cmd = r"""
    \coordinate (a) at ({x},{y});
    \coordinate (b) at ({xf},{yf});

    \path[{style}, {color}, line width={lw}, {headstyle}] (a) edge {options} (b);
    """

    #Convert px to pt (tikz doesn't accept px)
    cmdx = "%0.1fpt" % (float(convert_unit(str(dx))) * 72.27 / 96.0)
    cmdy = "%0.1fpt" % (float(convert_unit(str(dy))) * 72.27 / 96.0)

    if head_style != "":
        head_style = ">=%s" % (head_style)

    options = []
    if bend != None:
        options += ['bend %s' % bend]

    if in_angle != None:
        options += ['in=%0.1f' % in_angle]

    if out_angle != None:
        options += ['out=%0.1f' % out_angle]

    options = ','.join(options)
    if options != '':
        options = '[%s]' % options

    tikz_cmd = tikz_cmd.format(x=0,
                               y=0,
                               xf=cmdx,
                               yf=cmdy,
                               style=style,
                               color=color,
                               lw=lw,
                               headstyle=head_style,
                               options=options)

    #Run tikz command
    if dx < 0:
        tmpanchor = 'right'
    else:
        tmpanchor = 'left'

    if dy < 0:
        tmpanchor += '_top'
    else:
        tmpanchor += '_bottom'

    return tikz(tikz_cmd, x=x, y=y, figure_anchor=tmpanchor)
Пример #19
0
    def convert_position(self):

        #Function to convert position of an element
        tmpx = DEFAULT_X.copy()
        tmpy = DEFAULT_Y.copy()
        slidects = document._contents[gcs()]['contents']

        #Get the previous content if it exist (to us "+xx" or "-yy" in x, y coords)
        if self.id_index > 0:
            prev_ct = slidects[document._contents[gcs()]['element_keys'][self.id_index - 1]]
        else:
            prev_ct = None

        #Check if x or y are only floats
        if type(self.x) == type(float()) or type(self.x) == type(int()):
            tmpx['shift'] = self.x

        elif type(self.x) == type(dict()):
            tmpx = dict_deep_update(tmpx, self.x)

        elif type(self.x) == type(str()):

            converted = False

            if '+' in self.x:
                self.x = convert_unit( self.x.replace('+','') )
                #Make relative placement
                if prev_ct != None:
                    dict_old = prev_ct['positionner'].right + float( self.x )
                    tmpx = dict_deep_update(tmpx, dict_old)
                else:
                    tmpx['shift'] = float( self.x )

                tmpx['unit'] = 'px'
                converted = True

            if '-' in self.x:
                self.x = convert_unit( self.x.replace('-','') )
                #Make relative placement
                if prev_ct != None:
                    dict_old = prev_ct['positionner'].left - float( self.x )
                    tmpx = dict_deep_update(tmpx, dict_old)
                else:
                    tmpx['shift'] = float( self.x )
                tmpx['unit'] = 'px'
                converted = True


            if self.x in ['auto', 'center']:
                tmpx['shift'] = 0
                tmpx['align'] = self.x
                converted = True

            if not converted:
                try:
                    tmpx['shift'] = float( convert_unit(self.x) )
                    tmpx['unit'] = 'px'
                except:
                    print('[Error] x position is incorect string format')
                    print(self.x)

        else:
            print("[Error] x position need to be a float or a dict")


        if type(self.y) == type(float()) or type(self.y) == type(int()):
            tmpy['shift'] = self.y

        elif type(self.y) == type(dict()):
            tmpy = dict_deep_update(tmpy, self.y)

        elif type(self.y) == type(str()):

            converted = False
            if '+' in self.y:
                self.y = convert_unit( self.y.replace('+','') )
                #Make relative placement
                if prev_ct != None:
                    dict_old = prev_ct['positionner'].bottom + float(self.y)
                    tmpy = dict_deep_update(tmpy, dict_old)
                else:
                    tmpy['shift'] = float( self.y )

                tmpy['unit'] = 'px'
                converted = True

            if '-' in self.y:
                self.y = convert_unit( self.y.replace('-','') )
                #Make relative placement
                if prev_ct != None :
                    dict_old = prev_ct['positionner'].top - float(self.y)
                    tmpy = dict_deep_update(tmpy, dict_old)
                else:
                    tmpy['shift'] = float( self.y )
                tmpy['unit'] = 'px'
                converted = True

            if self.y in ['auto', 'center']:
                tmpy['shift'] = 0
                tmpy['align'] = self.y
                converted = True

            if not converted:
                try:
                    tmpy['shift'] = float( convert_unit(self.y) )
                    tmpy['unit'] = 'px'
                except:
                    print('[Error] y position is incorect string format')
                    print self.y
        else:
            print("[Error] y position need to be a float or an int or a dict")


        #Store the dict for positions
        self.x = tmpx
        self.y = tmpy

        #Convert position unit to pt
        if self.x['unit'] in ['cm', 'pt', 'mm']:
            self.x['shift'] = float( convert_unit( '%f%s'%(self.x['shift'], self.x['unit']) ) )

        if self.y['unit'] in ['cm', 'pt', 'mm']:
            self.y['shift'] = float( convert_unit( '%f%s'%(self.y['shift'], self.y['unit']) ) )

        if type(self.width) == type(str()):
            self.width = float( convert_unit(self.width) )

        if type(self.height) == type(str()):
            self.height = float( convert_unit(self.height) )
Пример #20
0
    def __init__(self, title=None, **kwargs):

        #Add a slide to the global counter
        if 'slide' in document._global_counter:
            document._global_counter['slide'] += 1
        else:
            document._global_counter['slide'] = 0
        #Init group counter
        document._global_counter['group'] = 0

        #check args from THEME
        self.args = check_function_args(slide, kwargs)

        out = {
            'title': title,
            'contents': {},
            'num': document._global_counter['slide'] + 1,
            'groups': [],
            "args": self.args,
            'htmlout': '',  #store rendered htmlelements inside the slide
            'animout': [],  #store svg rendered part of animatesvg
            'scriptout': '',  #store javascript defined in element['script']
            'cpt_anim': 0,
            'element_keys': []  #list to store elements id in order
        }

        #The id for this slide
        self.id = gcs()
        self.slide_num = document._global_counter['slide']

        #Change from dict to class
        self.tmpout = out
        self.contents = out['contents']
        self.element_keys = out['element_keys']
        self.cpt_anim = 0
        self.num = out['num']
        self.title = title

        #Store all outputs
        self.svgout = []
        self.htmlout = []
        self.scriptout = []
        self.animout = []
        self.svgheader = ''
        self.svgfooter = '\n</svg>\n'

        #Do we need to render the THEME layout on this slide
        self.render_layout = True

        #If we want to add background slide decodaration like header-bar or footer informations
        self.cpt_anim = 0
        self.groups = []

        #Add the slide to the document contents list
        document._contents[self.id] = out
        document._slides[self.id] = self

        if title != None:
            from beampy.modules.title import title as bptitle
            bptitle(title)
            self.ytop = float(convert_unit(self.title.reserved_y))
        else:
            self.ytop = 0
Пример #21
0
    def __init__(self, content, ext=None, **kwargs):
        # The type of the module
        self.type = 'svg'

        # Add the extra args to the module
        self.check_args_from_theme(kwargs)
        self.ext = ext

        # Register the content
        self.content = content

        # Special args for cache id
        self.args_for_cache_id = ['width', 'ext']

        # Check if the given filename is a string
        if isinstance(self.content, str):
            # Check extension
            self.ext = guess_file_type(self.content, self.ext)

        else:
            # Check kind of objects that are passed to filename
            # Bokeh plot
            if "bokeh" in str(type(self.content)):
                self.ext = 'bokeh'

            # Mathplotlib figure
            if "matplotlib" in str(type(self.content)):
                self.ext = "matplotlib"

        ######################################

        # Check if the input filename can be treated

        if self.ext is None:
            print("figure format can't be guessed.")
            sys.exit(1)

        # Bokeh image
        if self.ext == 'bokeh':
            self.type = 'html'

            # Todo get width and height from a bokeh figure
            if self.width is None:
                self.width = int(self.content.plot_width)
            if self.height is None:
                self.height = int(self.content.plot_height)

            # Do not cache this element if it's bokeh plot
            self.cache = False

        # Mpl figure
        if self.ext == 'matplotlib':

            # import close to force the closing of the input figure
            from matplotlib.pyplot import close
            close(self.content)  #close the figure

            # Set figure default width when it was not given as arguement
            if self.width is None:
                width_inch, height_inch = self.content.get_size_inches()
                self.width = convert_unit("%fin" % (width_inch))

            # Create a special args to create a unique id for caching

            # Generate the figure (in binary format as jpg) from the canvas
            with BytesIO() as tmpb:
                self.content.canvas.print_jpg(tmpb)
                tmpb.seek(0)
                md5t = hashlib.md5(tmpb.read()).hexdigest()
                #print(md5t)

            # Add this new arg
            self.args['mpl_fig_hash'] = md5t
            self.mpl_fig_hash = md5t
            self.args_for_cache_id += ['mpl_fig_hash']

        # Other filetype images
        if self.ext not in ('matplotlib', 'bokeh'):

            # Add file timestamp to an arguments for caching
            fdate = str(os.path.getmtime(self.content))
            self.args['filedate'] = fdate
            self.filedate = fdate
            self.args_for_cache_id += ['filedate']

            if self.width is None and self.height is None:
                self.width = document._slides[gcs()].curwidth

        # Add this module to the current slide + add positionner
        self.register()
Пример #22
0
    def render(self):
        """
            function to render figures
        """

        # Svg // pdf render
        if self.ext in ('svg', 'pdf', 'eps', 'matplotlib'):
            #Convert pdf to svg
            if self.ext == 'pdf':
                figurein = convert_pdf_to_svg(self.content)
            elif self.ext == 'eps':
                figurein = convert_eps_to_svg(self.content)

            #Convert matplotlib figure to svg
            elif self.ext == 'matplotlib':

                #Store mpl svg to a stringIO object
                with StringIO() as tmpf:
                    self.content.savefig(tmpf,
                                         bbox_inches='tight',
                                         format='svg')
                    tmpf.seek(0)  #go to the biginig of the file

                    #store tmpf content as string in figurein variable
                    figurein = tmpf.read().encode('utf-8')

            #General case for svg format
            else:
                #Check if a filename is given for a svg file or directly read the content value
                if os.path.isfile(self.content):
                    with open(self.content, 'r') as f:
                        figurein = f.read()

                else:
                    figurein = self.content

            #test if we need to optimise the svg
            if document._optimize_svg:
                figurein = optimize_svg(figurein)

            soup = BeautifulSoup(figurein, 'xml')

            #Change id in svg defs to use the global id system
            soup = make_global_svg_defs(soup)

            #Optimize the size of embeded svg images !
            if document._resize_raster:
                imgs = soup.findAll('image')
                if imgs:
                    for img in imgs:

                        #True width and height of embed svg image
                        width, height = int(float(img['width'])), int(
                            float(img['height']))
                        img_ratio = height / float(width)
                        b64content = img['xlink:href']

                        try:
                            in_img = BytesIO(
                                base64.b64decode(
                                    b64content.split(';base64,')[1]))
                            tmp_img = Image.open(in_img)
                            #print(tmp_img)
                            out_img = resize_raster_image(
                                tmp_img,
                                max_width=self.positionner.width.value)
                            out_b64 = base64.b64encode(
                                out_img.read()).decode('utf8')

                            #replace the resized image into the svg
                            img['xlink:href'] = 'data:image/%s;base64, %s' % (
                                tmp_img.format.lower(), out_b64)
                        except:
                            print('Unable to reduce the image size')
                            pass

            svgtag = soup.find('svg')

            svg_viewbox = svgtag.get("viewBox")

            tmph = svgtag.get("height")
            tmpw = svgtag.get("width")
            if tmph is None or tmpw is None:
                with tempfile.NamedTemporaryFile(mode='w',
                                                 prefix='beampytmp',
                                                 suffix='.svg') as f:
                    try:
                        f.write(figurein)
                    except Exception as e:
                        #python 2
                        f.write(figurein.encode('utf8'))

                    # force to write file content to disk
                    f.file.flush()

                    # get svg size using inkscape
                    tmph = getsvgheight(f.name)
                    tmpw = getsvgwidth(f.name)

            svgheight = convert_unit(tmph)
            svgwidth = convert_unit(tmpw)

            if svg_viewbox is not None:
                svgheight = svg_viewbox.split(' ')[3]
                svgwidth = svg_viewbox.split(' ')[2]

            # BS4 get the svg tag content without <svg> and </svg>
            tmpfig = svgtag.renderContents().decode('utf8')

            # Scale the figure according to the given width
            if self.width.value is not None and self.height.value is None:
                # SCALE OK need to keep the original viewBox !!!
                scale = (self.positionner.width / float(svgwidth)).value
                figure_height = float(svgheight) * scale
                figure_width = self.positionner.width.value

            # Scale the figure according to the given height
            if self.height.value is not None and self.width.value is None:
                figure_height = self.positionner.height.value
                scale = (self.positionner.height / float(svgheight)).value
                figure_width = float(svgwidth) * scale

            # Dont scale the figure let the user fix the width height
            if self.height.value is not None and self.width.value is not None:
                output = tmpfig
                figure_height = self.positionner.height.value
                figure_width = self.positionner.width.value
            else:
                # Apply the scaling to the figure
                tmphead = '\n<g transform="scale(%0.5f)">' % (scale)
                output = tmphead + tmpfig + '</g>\n'

            #Update the final svg size
            self.update_size(figure_width, figure_height)
            #Add the final svg output of the figure
            self.svgout = output

        #Bokeh images
        if self.ext == 'bokeh':

            # Change the sizing mode (need scale_both) to adapt size of the figure
            self.content.sizing_mode = 'scale_both'
            # Run the bokeh components function to separate figure html div and js script
            figscript, figdiv = components(self.content, wrap_script=False)

            # Transform figscript to givea function name load_bokehjs
            tmp = figscript.splitlines()
            goodscript = '\n'.join(['["load_bokeh"] = function() {'] +
                                   tmp[1:-1] + ['};\n'])

            #Add the htmldiv to htmlout
            self.htmlout = "<div id='bk_resizer' width='{width}px' height='{height}px' style='width: {width}px; height: {height}px; transform-origin: left top 0px;'> {html} </div>"
            self.htmlout = self.htmlout.format(width=self.positionner.width,
                                               height=self.positionner.height,
                                               html=figdiv)

            #Add the script to scriptout
            self.jsout = goodscript

        #For the other format
        if self.ext in ('png', 'jpeg', 'gif'):
            #Open image with PIL to compute size
            tmp_img = Image.open(self.content)
            _, _, tmpwidth, tmpheight = tmp_img.getbbox()

            # Scale the figure according to the given width
            if self.width.value is not None and self.height.value is None:
                # SCALE OK need to keep the original viewBox !!!
                scale = (self.positionner.width / float(tmpwidth)).value
                figure_height = float(tmpheight) * scale
                figure_width = self.positionner.width.value

            # Scale the figure according to the given height
            if self.height.value is not None and self.width.value is None:
                figure_height = self.positionner.height.value
                scale = (self.positionner.height / float(tmpheight)).value
                figure_width = float(tmpwidth) * scale

            # Dont scale the figure let the user fix the width height
            if self.height.value is not None and self.width.value is not None:
                figure_height = self.positionner.height.value
                figure_width = self.positionner.width.value

            if document._resize_raster:
                #Rescale figure to the good size (to improve size and display speed)
                if self.ext == 'gif':
                    print('Gif are not resized, the original size is taken!')
                    with open(self.content, "rb") as f:
                        figurein = base64.b64encode(f.read()).decode('utf8')

                else:
                    out_img = resize_raster_image(tmp_img,
                                                  max_width=figure_width)
                    figurein = base64.b64encode(out_img.read()).decode('utf8')
                    out_img.close()
            else:
                with open(self.content, "rb") as f:
                    figurein = base64.b64encode(f.read()).decode('utf8')

            tmp_img.close()

            if self.ext == 'png':
                output = '<image x="0" y="0" width="%s" height="%s" xlink:href="data:image/png;base64, %s" />' % (
                    figure_width, figure_height, figurein)

            if self.ext == 'jpeg':
                output = '<image x="0" y="0" width="%s" height="%s" xlink:href="data:image/jpg;base64, %s" />' % (
                    figure_width, figure_height, figurein)

            if self.ext == 'gif':
                output = '<image x="0" y="0" width="%s" height="%s" xlink:href="data:image/gif;base64, %s" />' % (
                    figure_width, figure_height, figurein)

            # Update the final size of the figure
            self.update_size(figure_width, figure_height)
            # Add the final svg to svgout
            self.svgout = output

        #print self.width, self.height
        #Update the rendered state of the module
        self.rendered = True
Пример #23
0
    def __init__(self, files_folder, **kwargs):
        """
            Function to create svg animation from a folder containing svg files

            - files_folder: Folder containing svg like "./my_folder/"

            - x['center']: x coordinate of the image
                           'center': center image relative to document._width
                           '+1cm": place image relative to previous element

            - y['auto']: y coordinate of the image
                         'auto': distribute all slide element on document._height
                         'center': center image relative to document._height (ignore other slide elements)
                         '+3cm': place image relative to previous element

            - start[0]: svg image number to start the sequence
            - end['end']: svg image number to stop the sequence
            - width[None]: Width of the figure (None = slide width)
            - fps[25]: animation framerate
            - autoplay[False]: autoplay animation when slide is displayed

        """

        #Add type
        self.type = 'animatesvg'

        #Check input args for this module
        self.check_args_from_theme(kwargs)

        #Cache is useless because we call figure function which handle the cache for each figures
        self.cache = False

        input_width = self.width  #Save the input width for mpl figures
        if self.width == None:
            self.width = document._width

        #Read all files from a given wildcard
        if type(files_folder) == type(str()):
            svg_files = glob.glob(files_folder)

            #Need to sort using the first digits finded in the name
            svg_files = sorted(
                svg_files, key=lambda x: int(''.join(re.findall(r'\d+', x))))

        #If the input is a list of names or mpl figures or other compatible with figure
        elif type(files_folder) == type(list()):
            svg_files = files_folder

            if input_width == None:
                width_inch, height_inch = files_folder[0].get_size_inches()
                self.width = convert_unit("%fin" % (width_inch))

        else:
            print('Unknow input type for files_folder')
            sys.exit(0)

        #check how many images we wants
        if self.end == 'end':
            self.end = len(svg_files)

        #Add content
        self.content = svg_files[self.start:self.end]

        #Register the module
        self.register()
Пример #24
0
    def render(self):
        """
            function to render figures
        """


        #Svg // pdf render
        if self.ext in ('svg', 'pdf') :
            #Convert pdf to svg
            if self.ext == 'pdf' :
                figurein = convert_pdf_to_svg( self.content )
            else:
                #Check if a filename is given for a svg file or directly read the content value
                if os.path.isfile(self.content):
                    with open(self.content) as f:
                        figurein = f.read()
                else:
                    figurein = self.content

            #test if we need to optimise the svg
            if document._optimize_svg:
                figurein = optimize_svg(figurein)

            soup = BeautifulSoup(figurein, 'xml')

            #Change id in svg defs to use the global id system
            soup = make_global_svg_defs(soup)

            #Optimize the size of embeded svg images !
            if document._resize_raster:
                imgs = soup.findAll('image')
                if imgs:
                    for img in imgs:

                        #True width and height of embed svg image
                        width, height = int( float(img['width']) ) , int( float(img['height']) )
                        img_ratio = height/float(width)
                        b64content = img['xlink:href']

                        try:
                            in_img =  BytesIO( base64.b64decode(b64content.split(';base64,')[1]) )
                            tmp_img = Image.open(in_img)
                            #print(tmp_img)
                            out_img = resize_raster_image( tmp_img )
                            out_b64 = base64.b64encode( out_img.read() )

                            #replace the resized image into the svg
                            img['xlink:href'] = 'data:image/%s;base64, %s'%(tmp_img.format.lower(), out_b64)
                        except:
                            print('Unable to reduce the image size')
                            pass

            svgtag = soup.find('svg')

            svg_viewbox = svgtag.get("viewBox")

            tmph = svgtag.get("height")
            tmpw = svgtag.get("width")
            if tmph == None or tmpw == None:
                fmpf, tmpname = tempfile.mkstemp(prefix="beampytmp")
                with open( tmpname+'.svg', 'w' ) as f:
                    f.write(figurein)
                    #print figurein
                tmph = getsvgheight( tmpname+'.svg' )
                tmpw = getsvgwidth( tmpname+'.svg' )
                #print tmpw, tmph
                os.remove(tmpname+'.svg')


            svgheight = convert_unit( tmph )
            svgwidth = convert_unit( tmpw )

            if svg_viewbox != None:
                svgheight = svg_viewbox.split(' ')[3]
                svgwidth = svg_viewbox.split(' ')[2]

            #SCALE OK need to keep the original viewBox !!!
            scale_x = self.positionner.width/float(svgwidth)
            #print svgwidth, svgheight, scale_x
            #scale_y = float(convert_unit(args['height']))/float(svgheight)
            good_scale = scale_x

            #BS4 get the svg tag content without <svg> and </svg>
            tmpfig = svgtag.renderContents()

            #Add the correct first line and last
            tmphead = '\n<g transform="scale(%0.5f)">'%(good_scale)
            output = tmphead + tmpfig + '</g>\n'

            figure_height = float(svgheight)*good_scale
            figure_width = self.width

            #Update the final svg size
            self.update_size(figure_width, figure_height)
            #Add the final svg output of the figure
            self.svgout = output

        #Bokeh images
        if self.ext == 'bokeh':

            #Run the bokeh components function to separate figure html div and js script
            figscript, figdiv = components(self.content, wrap_script=False)

            #Transform figscript to givea function name load_bokehjs
            tmp = figscript.splitlines()
            goodscript = '\n'.join( ['["load_bokeh"] = function() {'] + tmp[1:-1] + ['};\n'] )

            #Add the htmldiv to htmlout
            self.htmlout = figdiv
            #Add the script to scriptout
            self.jsout = goodscript

        #For the other format
        if self.ext in ('png', 'jpeg'):
            #Open image with PIL to compute size
            tmp_img = Image.open(self.content)
            _,_,tmpwidth,tmpheight = tmp_img.getbbox()
            scale_x = self.positionner.width/float(tmpwidth)
            figure_height = float(tmpheight) * scale_x
            figure_width = self.positionner.width

            if document._resize_raster:
                #Rescale figure to the good size (to improve size and display speed)
                out_img = resize_raster_image(tmp_img)
                figurein = base64.b64encode(out_img.read())
                out_img.close()
            else:
                with open( self.content, "r") as f:
                    figurein = base64.b64encode(f.read())

            tmp_img.close()

            if self.ext == 'png':
                output = '<image x="0" y="0" width="%s" height="%s" xlink:href="data:image/png;base64, %s" />'%(figure_width, figure_height, figurein)

            if self.ext == 'jpeg':
                output = '<image x="0" y="0" width="%s" height="%s" xlink:href="data:image/jpg;base64, %s" />'%(figure_width, figure_height, figurein)

            #Update the final size of the figure
            self.update_size(figure_width, figure_height)
            #Add the final svg to svgout
            self.svgout = output

        #print self.width, self.height
        #Update the rendered state of the module
        self.rendered = True
Пример #25
0
def arrow(x,
          y,
          dx,
          dy,
          style='->',
          color="black",
          lw='2pt',
          in_angle=None,
          out_angle=None,
          bend=None,
          head_style="",
          dashed=False):
    """
    Draw an arrow on slide. Ticks is used to render the arrow.

    Parameters
    ----------

    x : int or float or {'center', 'auto'} or str
        Horizontal position for the arrow. See positioning system of Beampy.

    y : int or float or {'center', 'auto'} or str
        Vertical position for the arrow. See positioning system of Beampy.

    dx : int or str
        Arrow horizontal displacement relative to x. `dx` could given in pixel
        as an integer or a float or in as string with an unit like '2cm'. It
        could be negative (left of x) or positive (right of x).

    dy : int or str
        Arrow vertical displacement relative to y. `dy` could given in pixel
        as an integer or a float or in as string with an unit like '2cm'. It
        could be negative (top of y) or positive (bottom of y).

    style : str in {'-', '->', '<-', '<->'}, optional
        Arrow style (the default value is '->'). The style is the one defined
        by Ticks style:

        * '->' or '<-', simple arrow.
        * '<->', two way arrows.
        * '-', a line.

    color : string, optional
        Arrow Color (the default value is 'black'). The color is given as Latex
        svgnames.

    lw : str or int, optional
        Arrow line width (the default is '2pt'). If the value is given as
        string followed by an unit, it is converted by beampy convert_unit
        function. It could also be given as an integer.

    in_angle : int or None, optional
        Angle at the end of the arrow (the default value is None which implies
        that the angle is automatically computed).

    out_angle : int or None, optional
        Starting angle of the arrow (the default value is None which implies
        that the angle is automatically computed).

    bend : {'left' or 'right'} or None, optional
        Direction of arrow bending (the default value is None, which implies a
        straight arrow). 'left' bends the arrow to the left and 'right' to the
        right.

    head_style : {'latex' or 'stealth' or ''}, optional
        Tikz head style of the arrow (the default value is '', which implies
        default head style of Ticks).

    dashed : True or False, optional
        Create a dashed arrow line (the default value is False).

    """

    tikz_cmd = r"""
    \coordinate (a) at ({x},{y});
    \coordinate (b) at ({xf},{yf});

    \path[{style}, {color}, line width={lw}, {headstyle}] (a) edge {options}(b);
    """

    # Convert px to pt (tikz doesn't accept px)
    cmdx = "%0.1fpt" % (float(convert_unit(str(dx))) * 72.27 / 96.0)
    cmdy = "%0.1fpt" % (-float(convert_unit(str(dy))) * 72.27 / 96.0)

    if head_style != "":
        head_style = ">=%s" % (head_style)

    options = []
    if bend is not None:
        options += ['bend %s' % bend]

    if in_angle is not None:
        options += ['in=%0.1f' % in_angle]

    if out_angle is not None:
        options += ['out=%0.1f' % out_angle]

    if dashed:
        options += ['dashed']

    options = ','.join(options)
    if options != '':
        options = '[%s]' % options

    tikz_cmd = tikz_cmd.format(x=0,
                               y=0,
                               xf=cmdx,
                               yf=cmdy,
                               style=style,
                               color=color,
                               lw=lw,
                               headstyle=head_style,
                               options=options)

    # Run tikz command
    if dx < 0 or (isinstance(dx, str) and '-' in dx):
        tmpanchor = 'right'
    else:
        tmpanchor = 'left'

    if dy < 0 or (isinstance(dy, str) and '-' in dy):
        tmpanchor += '_bottom'
    else:
        tmpanchor += '_top'

    return tikz(tikz_cmd, x=x, y=y, figure_anchor=tmpanchor)
Пример #26
0
def render_figure( ct ):

    """
        function to render figures
    """

    #read args in the dict
    args = ct['args']

    #Svg // pdf render
    if args['ext'] in ('svg', 'pdf') :

        #Convert pdf to svg
        if args['ext'] == 'pdf' :
            figurein = convert_pdf_to_svg( args['filename'] )
        else:
            #Check if a filename is given for a svg file or directly read the content value
            if 'filename' in args:
                with open(args['filename']) as f:
                    figurein = f.read()
            else:
                figurein = ct['content']

        #test if we need to optimise the svg
        if document._optimize_svg:
            figurein = optimize_svg(figurein)

        soup = BeautifulSoup(figurein, 'xml')

        #Change id in svg defs to use the global id system
        soup = make_global_svg_defs(soup)

        #Optimize the size of embeded svg images !
        if document._resize_raster:
            imgs = soup.findAll('image')
            if imgs:
                for img in imgs:
                    #True width and height of embed svg image
                    width, height = int( float(img['width']) ) , int( float(img['height']) )
                    img_ratio = height/float(width)
                    b64content = img['xlink:href']

                    try:
                        in_img =  BytesIO( base64.b64decode(b64content.split(';base64,')[1]) )
                        tmp_img = Image.open(in_img)
                        #print(tmp_img)
                        out_img = resize_raster_image( tmp_img )
                        out_b64 = base64.b64encode( out_img.read() )

                        #replace the resized image into the svg
                        img['xlink:href'] = 'data:image/%s;base64, %s'%(tmp_img.format.lower(), out_b64)
                    except:
                        print('Unable to reduce the image size')
                        pass

        svgtag = soup.find('svg')

        svg_viewbox = svgtag.get("viewBox")

        tmph = svgtag.get("height")
        tmpw = svgtag.get("width")
        if tmph == None or tmpw == None:
            fmpf, tmpname = tempfile.mkstemp(prefix="beampytmp")
            with open( tmpname+'.svg', 'w' ) as f:
                f.write(figurein)
                #print figurein
            tmph = getsvgheight( tmpname+'.svg' )
            tmpw = getsvgwidth( tmpname+'.svg' )
            #print tmpw, tmph
            os.remove(tmpname+'.svg')


        svgheight = convert_unit( tmph )
        svgwidth = convert_unit( tmpw )

        if svg_viewbox != None:
            svgheight = svg_viewbox.split(' ')[3]
            svgwidth = svg_viewbox.split(' ')[2]

        #SCALE OK need to keep the original viewBox !!!
        scale_x = ct['positionner'].width/float(svgwidth)
        #print svgwidth, svgheight, scale_x
        #scale_y = float(convert_unit(args['height']))/float(svgheight)
        good_scale = scale_x

        #BS4 get the svg tag content without <svg> and </svg>
        tmpfig = svgtag.renderContents()

        #print tmpfig[:100]

        #Add the correct first line and last
        #tmphead = '<g  transform="matrix(%s,0,0,%s,%s,%s)" viewBox="%s">'%(str(good_scale), str(good_scale), convert_unit(args['x']), convert_unit(args['y']), svg_viewbox))
        tmphead = '\n<g transform="scale(%0.5f)">'%(good_scale)
        output = tmphead + tmpfig + '</g>\n'

        figure_height = float(svgheight)*good_scale
        figure_width = ct['positionner'].width

    #Bokeh images
    if args['ext'] == 'bokeh':
        figurein = ct['content']
        figure_height = ct['positionner'].height
        figure_width =  ct['positionner'].width
        output = """%s"""%figurein


    #For the other format
    if args['ext'] in ['png', 'jpeg']:
        #Open image with PIL to compute size
        tmp_img = Image.open(args['filename'])
        _,_,tmpwidth,tmpheight = tmp_img.getbbox()
        scale_x = ct['positionner'].width/float(tmpwidth)
        figure_height = float(tmpheight) * scale_x
        figure_width = ct['positionner'].width

        if document._resize_raster:
            #Rescale figure to the good size (to improve size and display speed)
            out_img = resize_raster_image(tmp_img)
            figurein = base64.b64encode(out_img.read())
            out_img.close()
        else:
            with open( args['filename'], "r") as f:
                figurein = base64.b64encode(f.read())

        tmp_img.close()

    if args['ext'] == 'png':
        output = '<image x="0" y="0" width="%s" height="%s" xlink:href="data:image/png;base64, %s" />'%(figure_width, figure_height, figurein)

    if args['ext'] == 'jpeg':
        output = '<image x="0" y="0" width="%s" height="%s" xlink:href="data:image/jpg;base64, %s" />'%(figure_width, figure_height, figurein)

    ct['positionner'].update_size(figure_width, figure_height)

    return output
Пример #27
0
    def __init__(self, content, ext=None, **kwargs):
        """
            Add figure to current slide
            Accepted format: [svg, png, jpeg, bokeh figure]

            - x['center']: x coordinate of the image
                           'center': center image relative to document._width
                           '+1cm": place image relative to previous element

            - y['auto']: y coordinate of the image
                         'auto': distribute all slide element on document._height
                         'center': center image relative to document._height (ignore other slide elements)
                         '+3cm': place image relative to previous element

            - width[None]: Image width (None is the size of the image)

            - ext[None]: Image format, if None, format is guessed from filename.

        """

        #The type of the module
        self.type = 'svg'

        #Add the extra args to the module
        self.check_args_from_theme(kwargs)
        self.ext = ext

        #Register the content
        self.content = content

        #Special args for cache id
        self.args_for_cache_id = ['width', 'ext']

        #Check if the given filename is a string
        if type(self.content) == type(''):
            #Check extension

            self.ext = guess_file_type(self.content, self.ext)

        else:
            #Check kind of objects that are passed to filename

            #Bokeh plot
            if "bokeh" in str(type(self.content)):
                self.ext = 'bokeh'

            #Mathplotlib figure
            if "matplotlib" in str(type(self.content)):
                self.ext = "matplotlib"

        ######################################

        #Check if the input filename can be treated
        if self.ext == None:
            print("figure format can't be guessed.")
            sys.exit(1)

        #Bokeh image
        elif self.ext == 'bokeh':
            #print('I got a bokeh figure')
            figscript, figdiv = components(self.content, wrap_script=False)

            #Todo get width and height from a bokeh figure
            if self.width == None:
                self.width = int(self.content.plot_width)
            if self.height == None:
                self.height = int(self.content.plot_height)

            #Do not cache this element if it's bokeh plot
            self.cache = False

        #Mpl figure
        elif self.ext == 'matplotlib':

            #import close to force the closing of the input figure
            from matplotlib.pyplot import close
            close(self.content)  #close the figure

            #Set figure default width when it was not given as arguement
            if self.width == None:
                width_inch, height_inch = self.content.get_size_inches()
                self.width = convert_unit("%fin" % (width_inch))

            #Create a special args to create a unique id for caching

            #Generate the figure (in binary format as jpg) from the canvas
            with BytesIO() as tmpb:
                self.content.canvas.print_jpg(tmpb)
                tmpb.seek(0)
                md5t = hashlib.md5(tmpb.read()).hexdigest()
                #print(md5t)

            #Add this new arg
            self.args['mpl_fig_hash'] = md5t
            self.mpl_fig_hash = md5t
            self.args_for_cache_id += ['mpl_fig_hash']

        #Other filetype images
        else:

            #Add file timestamp to an arguments for caching
            fdate = str(os.path.getmtime(self.content))
            self.args['filedate'] = fdate
            self.filedate = fdate
            self.args_for_cache_id += ['filedate']

            if self.width == None:
                self.width = document._width

        #Add this module to the current slide + add positionner
        self.register()
Пример #28
0
def render_text( textin, args, usetex=True):
    """
        Function to render the text using latex

        latex -> dvi -> dvi2svgm -> svg

        Use the svg output for the text in the frame
    """

    #Check if their is width in args or if we need to use the default width
    if "width" in args:
        w = float(convert_unit(str(args['width'])))
    else:
        w = float(document._width)

    if usetex:

        #Check if a color is defined in args
        if 'fill' in args:
            if "#" in args['fill']:
                textin = r'{\color[HTML]{%s} %s }'%(args['fill'].replace('#','').upper(), textin)
            else:
                textin =r'{\color{%s} %s }'%(args['fill'], textin)

        if 'center' in args['align']:
            texalign = r'\centering'
        else:
            texalign = ''

        #fontsize{size}{interlinear_size}
        pretex = r"""
        \documentclass[crop=True]{standalone}
        \usepackage[utf8x]{inputenc}
        \usepackage{fix-cm}
        \usepackage{hyperref}
        \usepackage[svgnames]{xcolor}
        \renewcommand{\familydefault}{\sfdefault}
        \usepackage{varwidth}
        \usepackage{amsmath}
        \usepackage{amsfonts}
        \usepackage{amssymb}
        \special{html}
        \begin{document}
        \begin{varwidth}{%ipt}
        %s
        \fontsize{%i}{%i}\selectfont %s

        \end{varwidth}
        \end{document}
        """%(w*(72.27/96.),texalign,args['font-size'],(args['font-size']+args['font-size']*0.1),textin)
        #96/72.27 pt_to_px for latex

        
        #latex2svg
        testsvg = latex2svg( pretex )

        if testsvg == '':
            print("Latex Compilation Error")
            print("Beampy Input")
            print(pretex)
            sys.exit(0)
            
        #Parse the ouput with beautifullsoup
        soup = BeautifulSoup(testsvg, 'xml')
        svgsoup = soup.find('svg')

        #Get id of paths element to make a global counter over the entire document
        if 'path' not in document._global_counter:
            document._global_counter['path'] = 0

        #Create unique_id_ with time
        text_id =  ("%0.2f"%time.time()).split('.')[-1]
        for path in soup.find_all('path'):
            pid = path.get('id')
            new_pid = '%s_%i'%(text_id, document._global_counter['path'])
            testsvg = re.sub(pid,new_pid, testsvg)
            #path['id'] = new_pid //Need to change also the id ine each use elements ... replace (above) is simpler
            document._global_counter['path'] += 1

        #Reparse the svg
        soup = BeautifulSoup(testsvg, 'xml')

        #Change id in svg defs to use the global id system
        soup = make_global_svg_defs(soup)

        svgsoup = soup.find('svg')

        xinit, yinit, text_width, text_height = svgsoup.get('viewBox').split()
        text_width = float(text_width)
        text_height = float(text_height)



        #Use the first <use> in svg to get the y of the first letter
        try:
            uses = soup.find_all('use')
        except:
            print soup

        if len(uses) > 0:
            #TODO: need to make a more fine definition of baseline
            baseline = 0
            for use in uses:
                if use.has_attr('y'):
                    baseline = float(use.get('y'))
                    break

            if baseline == 0:
                print("Baseline one TeX error and is put to 0")
                #print baseline

            #Get the group tag to get the transform matrix to add yoffset
            g = soup.find('g')
            transform_matrix = g.get('transform')


            if 'va' in args and args['va'] == 'baseline':
                yoffset = - float(baseline)
                xoffset = - float(xinit)
                #for the box plot (see boxed below)
                oldyinit = yinit
                yinit = - float(baseline) + float(yinit)
                baseline = -float(oldyinit) + float(baseline)

            else:
                yoffset = -float(yinit)
                xoffset = -float(xinit)
                #For the box plot
                baseline = -float(yinit) + float(baseline)
                yinit = 0



            #print baseline, float(yinit), yoffset
            #newmatrix = 'translate(%s,%0.4f)'%(-float(xoffset),-float(yoffset) )
            tex_pt_to_px = 96/72.27
            newmatrix = 'scale(%0.3f) translate(%0.1f,%0.1f)'%(tex_pt_to_px, xoffset, yoffset)
            g['transform'] = newmatrix
            text_width = text_width * tex_pt_to_px
            text_height = text_height * tex_pt_to_px
            baseline = baseline * tex_pt_to_px
            yinit = yinit * tex_pt_to_px
            #g['viewBox'] = svgsoup.get('viewBox')

        output = svgsoup.renderContents()

        #Add red box around the text
        if document._text_box:

            boxed = '''<g transform="translate(%0.1f,%0.1f)">
            <line x1="0" y1="0" x2="%i" y2="0" style="stroke: red"/>
            <line x1="%i" y1="0" x2="%i" y2="%i" style="stroke: red"/>
            <line x1="%i" y1="%i" x2="0" y2="%i" style="stroke: red"/>
            <line x1="0" y1="%i" x2="0" y2="0" style="stroke: red"/>
            <line x1="0" y1="%i" x2="%i" y2="%i" style="stroke: green"/>
            </g>'''
            output += boxed%( 0, float(yinit),
                             text_width,
                             text_width,text_width,text_height,
                             text_width,text_height,text_height,
                             text_height,
                             baseline,text_width,baseline)

        #print output
    else:
        #Render as svg text
        args['x'] = convert_unit(args['x'])
        args['y'] = convert_unit(args['y'])
        args = ' '.join( [str(arg)+"='"+str(val)+"'" for arg, val in args.iteritems()] )
        output = "<text %s>%s</text>"%(args, textin.decode('utf-8'))
        #TODO: Need to fix the estimation of te width
        #print("[WARNING!!!] Width of classic svg text can't be estimated")
        text_width = 0
        text_height = 0

    return output, text_width, text_height
Пример #29
0
    def render(self):
        """
            function to render figures
        """


        #Svg // pdf render
        if self.ext in ('svg', 'pdf', 'matplotlib') :
            #Convert pdf to svg
            if self.ext == 'pdf' :
                figurein = convert_pdf_to_svg( self.content )

            #Convert matplotlib figure to svg
            elif self.ext == 'matplotlib':

                #Store mpl svg to a stringIO object
                with StringIO() as tmpf:
                    self.content.savefig(tmpf, bbox_inches='tight', format='svg')
                    tmpf.seek(0) #go to the biginig of the file

                    #store tmpf content as string in figurein variable
                    figurein = tmpf.read().encode('utf-8')

            #General case for svg format
            else:
                #Check if a filename is given for a svg file or directly read the content value
                if os.path.isfile(self.content):
                    with open(self.content) as f:
                        figurein = f.read()
                else:
                    figurein = self.content

            #test if we need to optimise the svg
            if document._optimize_svg:
                figurein = optimize_svg(figurein)

            soup = BeautifulSoup(figurein, 'xml')

            #Change id in svg defs to use the global id system
            soup = make_global_svg_defs(soup)

            #Optimize the size of embeded svg images !
            if document._resize_raster:
                imgs = soup.findAll('image')
                if imgs:
                    for img in imgs:

                        #True width and height of embed svg image
                        width, height = int( float(img['width']) ) , int( float(img['height']) )
                        img_ratio = height/float(width)
                        b64content = img['xlink:href']

                        try:
                            in_img =  BytesIO( base64.b64decode(b64content.split(';base64,')[1]) )
                            tmp_img = Image.open(in_img)
                            #print(tmp_img)
                            out_img = resize_raster_image( tmp_img )
                            out_b64 = base64.b64encode( out_img.read() )

                            #replace the resized image into the svg
                            img['xlink:href'] = 'data:image/%s;base64, %s'%(tmp_img.format.lower(), out_b64)
                        except:
                            print('Unable to reduce the image size')
                            pass

            svgtag = soup.find('svg')

            svg_viewbox = svgtag.get("viewBox")

            tmph = svgtag.get("height")
            tmpw = svgtag.get("width")
            if tmph == None or tmpw == None:
                fmpf, tmpname = tempfile.mkstemp(prefix="beampytmp")
                with open( tmpname+'.svg', 'w' ) as f:
                    f.write(figurein)
                    #print figurein
                tmph = getsvgheight( tmpname+'.svg' )
                tmpw = getsvgwidth( tmpname+'.svg' )
                #print tmpw, tmph
                os.remove(tmpname+'.svg')


            svgheight = convert_unit( tmph )
            svgwidth = convert_unit( tmpw )

            if svg_viewbox != None:
                svgheight = svg_viewbox.split(' ')[3]
                svgwidth = svg_viewbox.split(' ')[2]

            #SCALE OK need to keep the original viewBox !!!
            scale_x = self.positionner.width/float(svgwidth)
            #print svgwidth, svgheight, scale_x
            #scale_y = float(convert_unit(args['height']))/float(svgheight)
            good_scale = scale_x

            #BS4 get the svg tag content without <svg> and </svg>
            tmpfig = svgtag.renderContents()

            #Add the correct first line and last
            tmphead = '\n<g transform="scale(%0.5f)">'%(good_scale)
            output = tmphead + tmpfig + '</g>\n'

            figure_height = float(svgheight)*good_scale
            figure_width = self.width

            #Update the final svg size
            self.update_size(figure_width, figure_height)
            #Add the final svg output of the figure
            self.svgout = output

        #Bokeh images
        if self.ext == 'bokeh':

            # Run the bokeh components function to separate figure html div and js script
            figscript, figdiv = components(self.content, wrap_script=False)

            # Transform figscript to givea function name load_bokehjs
            tmp = figscript.splitlines()
            goodscript = '\n'.join( ['["load_bokeh"] = function() {'] + tmp[1:-1] + ['};\n'] )

            #Add the htmldiv to htmlout
            self.htmlout = figdiv
            #Add the script to scriptout
            self.jsout = goodscript

        #For the other format
        if self.ext in ('png', 'jpeg'):
            #Open image with PIL to compute size
            tmp_img = Image.open(self.content)
            _,_,tmpwidth,tmpheight = tmp_img.getbbox()
            scale_x = self.positionner.width/float(tmpwidth)
            figure_height = float(tmpheight) * scale_x
            figure_width = self.positionner.width

            if document._resize_raster:
                #Rescale figure to the good size (to improve size and display speed)
                out_img = resize_raster_image(tmp_img)
                figurein = base64.b64encode(out_img.read())
                out_img.close()
            else:
                with open( self.content, "r") as f:
                    figurein = base64.b64encode(f.read())

            tmp_img.close()

            if self.ext == 'png':
                output = '<image x="0" y="0" width="%s" height="%s" xlink:href="data:image/png;base64, %s" />'%(figure_width, figure_height, figurein)

            if self.ext == 'jpeg':
                output = '<image x="0" y="0" width="%s" height="%s" xlink:href="data:image/jpg;base64, %s" />'%(figure_width, figure_height, figurein)

            #Update the final size of the figure
            self.update_size(figure_width, figure_height)
            #Add the final svg to svgout
            self.svgout = output

        #print self.width, self.height
        #Update the rendered state of the module
        self.rendered = True
Пример #30
0
def arrow(x, y, dx, dy, style='->', color="black", lw='2pt', in_angle=None, out_angle=None, bend=None, head_style=""):
    """
    function to draw arrows on slide

    Arguments
    ---------

    x: initial x position of the arrow
    y: initial y position

    dx: end of arrow relative to x
    dy: end of arrow relative to y

    style ["->"]: arrow style "-": a simple line
                              "->": simple arrow
                              "<->": two way arrows

    color ['black']: arrow color (you can use svgnames)

    lw ["2pt"]: set the line width in point

    in_angle  [None]: angle at the end of the arrow
    out_angle [None]: angle at the start of the arrow
    bend [None]: direction of arrow bending "left" or "right"

    head_style [""]: change head_style of arrow: "latex", "stealth"
    """



    tikz_cmd = r"""
    \coordinate (a) at ({x},{y});
    \coordinate (b) at ({xf},{yf});

    \path[{style}, {color}, line width={lw}, {headstyle}] (a) edge {options} (b);
    """


    #Convert px to pt (tikz doesn't accept px)
    cmdx = "%0.1fpt"%(float(convert_unit(str(dx))) * 72.27/96.0)
    cmdy = "%0.1fpt"%(float(convert_unit(str(dy))) * 72.27/96.0)

    if head_style != "":
        head_style = ">=%s"%(head_style)

    options=[]
    if bend != None:
        options += ['bend %s'%bend]

    if in_angle != None:
        options += ['in=%0.1f'%in_angle]

    if out_angle != None:
        options += ['out=%0.1f'%out_angle]

    options = ','.join(options)
    if options != '':
        options = '[%s]'%options

    tikz_cmd = tikz_cmd.format( x=0, y=0, xf=cmdx, yf=cmdy, style=style,
                                color=color, lw=lw, headstyle=head_style,
                                options=options )

    #Run tikz command
    if dx < 0:
        tmpanchor='right'
    else:
        tmpanchor='left'

    if dy < 0:
        tmpanchor += '_top'
    else:
        tmpanchor += '_bottom'

    return tikz( tikz_cmd, x=x, y=y, figure_anchor=tmpanchor)
Пример #31
0
def itemize(items_list, **kwargs):
    '''
    Generates a list or an enumeration.

    Parameters
    ----------

    items_list : list of str
        List of item sentences.

    x : int or float or {'center', 'auto'} or str, optional
        Horizontal position for the item list (the default is 'center'). See
        positioning system of Beampy.

    y : int or float or {'center', 'auto'} or str, optional
        Vertical position for the item list (the default is 'auto'). See
        positioning system of Beampy.

    width : int or float or None, optional
       Width of the group containing items (the default is None, which implies
       that the width is computed to fit the longest item width).

    item_style : {'bullet','number'} or str, optional
        Style of the item markers (the default theme sets this value to
        'bullet', which implies that item marker decorator is a bullet). The
        bullet could be replaced by any string, including latex symbols. When
        `item_style`='number', the item makers is an increasing number to
        create an enumeration.

    item_spacing : int or float or str, optional
        Vertical spacing between items (the default theme sets this value to
        '+1cm'). `item_spacing` accepts the same values as Beampy `x` or `y`.

    item_indent : int or float or str, optional
        Horizontal item indent (the default theme sets this value to '0cm').
        `item_indent` accepts the same values as Beampy `x` or `y`.

    item_color : str, optional
        Color of item marker (the default theme sets this value to
        doc._theme['title']['color']). Color could be given as svg-color-names
        or HTML color hex values (expl: #fffff for white).

    text_color : str, optional
        Color of the item texts (the default theme sets this value to
        doc._theme['text']['color']). Color could be given as svg-color-names
        or HTML color hex values (expl: #fffff for white).

    item_layers : (list of int or string) or None, optional
        Place items into layers to animate them (the default theme sets this
        value to None, which implies that all items are displayed on the same
        layer). The list should have the same length as the `items_list`. The
        item in `item_layers` list could refers to a given layer number, given
        as int, or use python list index syntax (like ':', ':-1', '3:') given
        as string.

        >>> itemize(['item1 on all layers', 'item2 on layer 1'],
                    item_layers=[':',1])

    '''

    args = check_function_args(itemize, kwargs)
    number = 1

    if args['width'] is not None:
        in_width = float(convert_unit(args['width'])) - float(
            convert_unit(args['item_indent']))
    else:
        in_width = float(document._width) - float(
            convert_unit(args['item_indent']))

    if args['item_layers'] is not None:
        if len(items_list) != len(args['item_layers']):
            raise ValueError(
                'Length of item_layers is not the same as the length of items_list'
            )

    with group(width=args['width'], x=args['x'], y=args['y']) as groupitem:

        for i, the_item in enumerate(items_list):

            if args['item_style'] == 'bullet':
                item_char = r'$\bullet$'

            elif args['item_style'] == 'number':
                item_char = str(number) + r'.'
                number += 1

            else:
                item_char = args['item_style']

            # Add color

            item_char = color_text(item_char, args['item_color'])
            the_item = color_text(the_item, args['text_color'])

            if i == 0:
                t = text(item_char + r' ' + the_item,
                         x=args['item_indent'],
                         y=0,
                         width=in_width)
            else:
                t = text(item_char + r' ' + the_item,
                         x=args['item_indent'],
                         y=args['item_spacing'],
                         width=in_width)

            # Add layers to item
            if args['item_layers'] is not None:
                layer = args['item_layers'][i]
                if isinstance(layer, str):
                    eval('t[%s]' % layer)
                else:
                    t[args['item_layers'][i]]

    return groupitem