Exemple #1
0
def transition(slide, spec: Union[str, dict], data: dict):
    '''
    Apply transition on slide based on spec. python-pptx does not have this feature, so we tweak
    XML directly.

    :arg Slide slide: slide to apply the transition to
    :arg dict/str spec: type of transition to apply. ``config.yaml`` lists transitions and options.
        It can also be a dict specifying ``type`` (type of transition), ``duration`` (in seconds)
        and ``advance`` (auto-advance after seconds)
    :arg dict data: data context for the ``spec`` expression
    '''
    if spec is None:
        return
    # Convert spec into this format: {type: ..., advance: ..., duration: ...}
    if isinstance(spec, str):
        spec = {'type': spec}
    if not isinstance(spec, dict):
        raise ValueError('transition: %r is not a str or dict' % spec)
    # conf is from config.yaml, and has all transition types and options
    conf = commands.conf
    type = commands.expr(spec.get('type', None), data)
    if type is not None:
        # Parse the type into OXML: "glitter diamond left" -> <glitter pattern="diamond" dir="r"/>
        tag, *options = type.split()
        attrs = {}
        if tag in conf['transition-alias']:
            attrs.update(conf['transition-alias'][tag])
            tag = attrs.pop('tag')
        if tag not in conf['transition']:
            raise ValueError('transition.type: %s is an unknown transition' %
                             type)
        trans = conf['transition'][tag]
        options = trans['default'] if (not options
                                       and 'default' in trans) else options
        for option in options:
            if option not in trans:
                raise ValueError(
                    'transition.type: "%s" has invalid option %s' %
                    (type, option))
            attrs.update(trans[option])
        # Remove existing transition
        el = slide.element.find(qn('mc:AlternateContent'))
        if el is not None:
            slide.element.remove(el)
        # Add transition OXML
        # TODO: fails on slides with equations, zoom, or any other mc:alternateContent
        if tag != 'none':
            ns = trans.get('ns', 'p')
            attrs = ' '.join('%s="%s"' % (k, v) for k, v in attrs.items())
            xml = conf['transition-tmpl'][ns].format(tag=tag, attrs=attrs)
            el = parse_xml(xml)[0]
            slide.element.append(el)
    # Add attributes for duration: and advance:
    for key, attr in (('duration', qn('p14:dur')), ('advance', 'advTm')):
        val = commands.expr(spec.get(key, None), data)
        if val is not None:
            trans = slide.element.find(
                'mc:AlternateContent/mc:Choice/p:transition', _nsmap)
            if trans is not None:
                trans.set(attr, '%s' % int(float(val) * 1000))
Exemple #2
0
    def iter_shape_elms(self):
        """
        Generate each child of this ``<p:spTree>`` element that corresponds
        to a shape, in the sequence they appear in the XML.
        """
        for elm in self.iterchildren():
            if elm.tag in self._shape_tags:
                yield elm

            if elm.tag == qn("ve:AlternateContent"):
                choice = elm.find(qn("ve:Choice"))

                if choice != None:
                    for content in choice.iterchildren():
                        #Choose first, among proper items
                        if content.tag in self._shape_tags:
                            yield content
                            break
                else:
                    fallback = elm.find(qn("ve:Fallback"))

                    if fallback != None:
                        for content in fallback.iterchildren():
                            # Choose first, among proper items
                            if content.tag in self._shape_tags:
                                yield content
                                break
Exemple #3
0
def set_font(run, font_name):
    run.font.name = font_name
    if run.font._rPr.find(qn('a:ea')) is not None:
        run.font._rPr.find(qn('a:ea')).set('typeface', run.font.name)
    else:
        element = run.font._rPr.makeelement(qn('a:ea'))
        element.set('typeface', run.font.name)
        run.font._rPr.append(element)
Exemple #4
0
 def clear_lum(self):
     """
     Return self after removing any <a:lumMod> and <a:lumOff> child
     elements.
     """
     lum_tagnames = (qn('a:lumMod'), qn('a:lumOff'))
     for child in self.getchildren():
         if child.tag in lum_tagnames:
             self.remove(child)
     return self
def set_text_frame_font(text_frame):
	for paragraph in text_frame.paragraphs:
		for run in paragraph.runs:
			if run.font.name in fonts_to_be_replaced:
				run.font.name = fonts_to_be_replaced[run.font.name]
				if run.font._rPr.find(qn('a:ea')) is not None:
					run.font._rPr.find(qn('a:ea')).set('typeface', run.font.name)
				else:
					element = run.font._rPr.makeelement(qn('a:ea'))
					element.set('typeface', run.font.name)
					run.font._rPr.append(element)
			elif run.font.name is None:
				run.font.name = '思源黑体'
Exemple #6
0
def fill_opacity(fill: FillFormat, val: Union[int, float, None]) -> None:
    '''Set the FillFormat opacity to a number'''
    if fill.type != MSO_FILL.SOLID:
        raise ValueError('Cannot set opacity: %r on non-solid fill type %r' % (val, fill.type))
    for tag in ('hslClr', 'sysClr', 'srgbClr', 'prstClr', 'scrgbClr', 'schemeClr'):
        color = fill._xPr.find('.//' + qn('a:%s' % tag))
        if color is not None:
            alpha = color.find(qn('a:alpha'))
            if alpha is None:
                alpha = OxmlElement('a:alpha')
                color.append(alpha)
            alpha.set('val', ST_Percentage.convert_to_xml(val))
            break
Exemple #7
0
 def _get_str_prop(self, name):
     """Return string value of *name* property."""
     # explicit class reference avoids another pass through getattribute
     tag = qn(CT_CoreProperties._str_tags[name])
     if not hasattr(self, tag):
         return ''
     return getattr(self, tag).text
Exemple #8
0
    def new_placeholder_sp(id_, name, ph_type, orient, sz, idx):
        """
        Return a new ``<p:sp>`` element tree configured as a placeholder
        shape.
        """
        xml = CT_Shape._ph_sp_tmpl % (id_, name)
        sp = parse_xml_bytes(xml)

        # placeholder shapes get a "no group" lock
        SubElement(sp.nvSpPr.cNvSpPr, 'a:spLocks')
        sp.nvSpPr.cNvSpPr[qn('a:spLocks')].set('noGrp', '1')

        # placeholder (ph) element attributes values vary by type
        ph = SubElement(sp.nvSpPr.nvPr, 'p:ph')
        if ph_type != PH_TYPE_OBJ:
            ph.set('type', ph_type)
        if orient != PH_ORIENT_HORZ:
            ph.set('orient', orient)
        if sz != PH_SZ_FULL:
            ph.set('sz', sz)
        if idx != 0:
            ph.set('idx', str(idx))

        placeholder_types_that_have_a_text_frame = (
            PH_TYPE_TITLE, PH_TYPE_CTRTITLE, PH_TYPE_SUBTITLE, PH_TYPE_BODY,
            PH_TYPE_OBJ)

        if ph_type in placeholder_types_that_have_a_text_frame:
            sp.append(CT_TextBody.new_txBody())

        objectify.deannotate(sp, cleanup_namespaces=True)
        return sp
Exemple #9
0
 def paragraphs(self):
     """
     Immutable sequence of |_Paragraph| instances corresponding to the
     paragraphs in this text frame. A text frame always contains at least
     one paragraph.
     """
     return tuple([_Paragraph(p, self) for p in self._txBody[qn('a:p')]])
Exemple #10
0
 def notify_height_changed(self):
     """
     Called by a row when its height changes, triggering the graphic frame
     to recalculate its total height (as the sum of the row heights).
     """
     new_table_height = sum([row.height for row in self.rows])
     self._graphicFrame.xfrm[qn('a:ext')].set('cy', str(new_table_height))
Exemple #11
0
 def add_sldId(self, rId):
     """
     Return a reference to a newly created <p:sldId> child element having
     its r:id attribute set to *rId*.
     """
     sldId = SubElement(self, 'p:sldId', id=self._next_id)
     sldId.set(qn('r:id'), rId)
     return sldId
Exemple #12
0
 def offset(self, value):
     if self._element.tag != qn("c:catAx"):
         raise ValueError("only a category axis has an offset")
     self._element._remove_lblOffset()
     if value == 100:
         return
     lblOffset = self._element._add_lblOffset()
     lblOffset.val = value
Exemple #13
0
 def notify_width_changed(self):
     """
     Called by a column when its width changes, triggering the graphic
     frame to recalculate its total width (as the sum of the column
     widths).
     """
     new_table_width = sum([col.width for col in self.columns])
     self._graphicFrame.xfrm[qn('a:ext')].set('cx', str(new_table_width))
Exemple #14
0
 def spPr(self):
     """
     The required <a:spPr> child element, raises if not present.
     """
     spPr = self.find(qn('p:spPr'))
     if spPr is None:
         # TODO: this should be ValidationError, not ValueError
         raise ValueError("pic element missing required spPr child")
     return spPr
def _NotesSlideShapeFactory(shape_elm, parent):
    """
    Return an instance of the appropriate shape proxy class for *shape_elm*
    on a notes slide.
    """
    tag_name = shape_elm.tag
    if tag_name == qn("p:sp") and shape_elm.has_ph_elm:
        return NotesSlidePlaceholder(shape_elm, parent)
    return BaseShapeFactory(shape_elm, parent)
Exemple #16
0
 def remove_child_r_elms(self):
     """
     Return self after removing all <a:r> child elements.
     """
     children = self.getchildren()
     for child in children:
         if child.tag == qn("a:r"):
             self.remove(child)
     return self
Exemple #17
0
def _MasterShapeFactory(shape_elm, parent):
    """
    Return an instance of the appropriate shape proxy class for *shape_elm*
    on a slide master.
    """
    tag_name = shape_elm.tag
    if tag_name == qn('p:sp') and shape_elm.has_ph_elm:
        return MasterPlaceholder(shape_elm, parent)
    return BaseShapeFactory(shape_elm, parent)
Exemple #18
0
def set_tooltip(type, prs, slide, shape, val):
    if val is None:
        return
    link = get_or_add_link(shape, type, 'hlinkClick')
    rid, action = link.get(qn('r:id'), None), link.get('action', None)
    # Note: link was just created, or has no action, we can't set a tooltip. So create a next: link
    if rid is None or action is conf['link-action']['noaction']:
        link = set_link(type, 'hlinkClick', prs, slide, shape, 'next')
    link.set('tooltip', val)
Exemple #19
0
def BaseShapeFactory(shape_elm, parent):
    """
    Return an instance of the appropriate shape proxy class for *shape_elm*.
    """
    tag = shape_elm.tag

    if tag == qn('p:pic'):
        videoFiles = shape_elm.xpath('./p:nvPicPr/p:nvPr/a:videoFile')
        if videoFiles:
            return Movie(shape_elm, parent)
        return Picture(shape_elm, parent)

    shape_cls = {
        qn('p:cxnSp'): Connector,
        qn('p:sp'): Shape,
        qn('p:graphicFrame'): GraphicFrame,
    }.get(tag, BaseShape)

    return shape_cls(shape_elm, parent)
Exemple #20
0
 def _set_revision(self, value):
     """Set integer value of revision property to *value*"""
     if not isinstance(value, int) or value < 1:
         tmpl = "revision property requires positive int, got '%s'"
         raise ValueError(tmpl % value)
     tag = qn('cp:revision')
     setattr(self, tag, str(value))
     # objectify will leave in a py: namespace without this cleanup
     elm = getattr(self, tag)
     objectify.deannotate(elm, cleanup_namespaces=True)
def _SlidePlaceholderFactory(shape_elm, parent):
    """
    Return a placeholder shape of the appropriate type for *shape_elm*.
    """
    tag = shape_elm.tag
    if tag == qn("p:sp"):
        Constructor = {
            PP_PLACEHOLDER.BITMAP: PicturePlaceholder,
            PP_PLACEHOLDER.CHART: ChartPlaceholder,
            PP_PLACEHOLDER.PICTURE: PicturePlaceholder,
            PP_PLACEHOLDER.TABLE: TablePlaceholder,
        }.get(shape_elm.ph_type, SlidePlaceholder)
    elif tag == qn("p:graphicFrame"):
        Constructor = PlaceholderGraphicFrame
    elif tag == qn("p:pic"):
        Constructor = PlaceholderPicture
    else:
        Constructor = BaseShapeFactory
    return Constructor(shape_elm, parent)
Exemple #22
0
 def _first_successor_in(self, *successor_tagnames):
     """
     Return the first child with tag in *successor_tagnames*, or None if
     not found.
     """
     for tagname in successor_tagnames:
         successor = self.find(qn(tagname))
         if successor is not None:
             return successor
     return None
Exemple #23
0
 def _first_child_found_in(self, *tagnames):
     """
     Return the first child found with tag in *tagnames*, or None if
     not found.
     """
     for tagname in tagnames:
         child = self.find(qn(tagname))
         if child is not None:
             return child
     return None
Exemple #24
0
def get_or_add_link(shape, type, event):
    # el is the lxml element of the shape. Can be ._element or ._run
    el = getattr(shape, conf['link-attrs'][type]['el'])
    # parent is the container inside which we insert the link. Can be p:cNvPr or a:rPr
    parent = el.find('.//' + conf['link-attrs'][type]['tag'], _nsmap)
    # Create link if required. (Else preserve all attributes one existing link, like tooltip)
    link = parent.find(qn('a:%s' % event))
    if link is None:
        link = OxmlElement('a:%s' % event)
        parent.insert(0, link)
    return link
Exemple #25
0
 def _add_solidFill(self):
     """
     Return a newly added <a:solidFill> child element.
     """
     solidFill = Element("a:solidFill")
     ln = self.find(qn("a:ln"))
     if ln is not None:
         self.insert(1, solidFill)
     else:
         self.insert(0, solidFill)
     return solidFill
Exemple #26
0
def _resize(elements, n):
    '''Ensure that a tr, tc or gridCol list has n children by cloning or deleting last element'''
    for index in range(len(elements), n, -1):
        elements[index - 1].delete()
    for index in range(len(elements), n):
        last_element = copy.deepcopy(elements[-1])
        # When copying the last element, remove the a:extLst. This may have a rowId and colId
        # Note: Anand isn't sure if this removes any useful properties. See https://bit.ly/2ZYDw7B
        for ext_lst in last_element.findall('.//' + qn('a:extLst')):
            ext_lst.getparent().remove(ext_lst)
        elements[-1].addnext(last_element)
Exemple #27
0
def BaseShapeFactory(shape_elm, parent):
    """
    Return an instance of the appropriate shape proxy class for *shape_elm*.
    """
    tag = shape_elm.tag

    if tag == qn("p:pic"):
        videoFiles = shape_elm.xpath("./p:nvPicPr/p:nvPr/a:videoFile")
        if videoFiles:
            return Movie(shape_elm, parent)
        return Picture(shape_elm, parent)

    shape_cls = {
        qn("p:cxnSp"): Connector,
        qn("p:grpSp"): GroupShape,
        qn("p:sp"): Shape,
        qn("p:graphicFrame"): GraphicFrame,
    }.get(tag, BaseShape)

    return shape_cls(shape_elm, parent)
Exemple #28
0
 def _set_str_prop(self, name, value):
     """Set string value of *name* property to *value*"""
     value = str(value)
     if len(value) > 255:
         tmpl = ("exceeded 255 char max length of property '%s', got:"
                 "\n\n'%s'")
         raise ValueError(tmpl % (name, value))
     tag = qn(CT_CoreProperties._str_tags[name])
     setattr(self, tag, value)
     # objectify will leave in a py: namespace without this cleanup
     elm = getattr(self, tag)
     objectify.deannotate(elm, cleanup_namespaces=True)
Exemple #29
0
 def _set_date_prop(self, name, value):
     """Set datetime value of *name* property to *value*"""
     if not isinstance(value, datetime):
         tmpl = ("'%s' property requires <type 'datetime.datetime'> objec"
                 "t, got %s")
         raise ValueError(tmpl % (name, type(value)))
     tagname = CT_CoreProperties._date_tags[name]
     tag = qn(tagname)
     dt_str = value.strftime('%Y-%m-%dT%H:%M:%SZ')
     setattr(self, tag, dt_str)
     # objectify will leave in a py: namespace without this cleanup
     elm = getattr(self, tag)
     objectify.deannotate(elm, cleanup_namespaces=True)
     if name in ('created', 'modified'):
         # these two require an explicit 'xsi:type' attribute
         # first and last line are a hack required to add the xsi
         # namespace to the root element rather than each child element
         # in which it is referenced
         self.set(qn('xsi:foo'), 'bar')
         self[tag].set(qn('xsi:type'), 'dcterms:W3CDTF')
         del self.attrib[qn('xsi:foo')]
Exemple #30
0
 def _add_noFill(self):
     """
     Return a newly added <a:noFill> child element, assuming no other fill
     EG_FillProperties element is present.
     """
     noFill = Element("a:noFill")
     ln = self.find(qn("a:ln"))
     if ln is not None:
         self.insert(1, noFill)
     else:
         self.insert(0, noFill)
     return noFill
Exemple #31
0
 def _clear_color_choice(self):
     """
     Remove the EG_ColorChoice child element, e.g. <a:schemeClr>.
     """
     eg_colorchoice_tagnames = (
         'a:scrgbClr', 'a:srgbClr', 'a:hslClr', 'a:sysClr', 'a:schemeClr',
         'a:prstClr'
     )
     for tagname in eg_colorchoice_tagnames:
         element = self.find(qn(tagname))
         if element is not None:
             self.remove(element)
Exemple #32
0
 def _set_hlinkClick(self, value):
     """
     For *value* is None, remove the ``<a:hlinkClick>`` child. For all
     other values, raise |ValueError|.
     """
     if value is not None:
         tmpl = "only None can be assigned to optional element, got '%s'"
         raise ValueError(tmpl % value)
     # value is None ----------------
     hlinkClick = self.find(qn("a:hlinkClick"))
     if hlinkClick is not None:
         self.remove(hlinkClick)
Exemple #33
0
def set_link(type, event, prs, slide, shape, val):
    if val is None:
        return
    link = get_or_add_link(shape, type, event)
    # Set link's r:id= and action= based on the type of the link
    if val in conf['link-action']:  # it's a ppaction://
        rid = link.get(qn('r:id'), None)
        if rid in slide.part.rels:
            slide.part.drop_rel(rid)
        link.set(qn('r:id'), '')
        link.set('action', conf['link-action'][val])
    elif isinstance(val, int) or val.isdigit():  # it's a slide
        slide_part = prs.slides[int(val) - 1].part
        link.set(qn('r:id'), slide.part.relate_to(slide_part, RT.SLIDE, False))
        link.set('action', 'ppaction://hlinksldjump')
    elif urlparse(val).netloc:  # it's a URL
        link.set(qn('r:id'), slide.part.relate_to(val, RT.HYPERLINK, True))
        link.attrib.pop('action', '')
    elif os.path.splitext(
            val)[-1].lower() in conf['link-ppt-files']:  # it's a PPT
        link.set(qn('r:id'), slide.part.relate_to(val, RT.HYPERLINK, True))
        link.set('action', 'ppaction://hlinkpres?slideindex=1&slidetitle=')
    else:  # it's a file
        link.set(qn('r:id'), slide.part.relate_to(val, RT.HYPERLINK, True))
        link.set('action', 'ppaction://hlinkfile')
    return link
Exemple #34
0
def flatten_group_transforms(shape, x=0, y=0, sx=1, sy=1):
    '''
    Groups re-scale child shapes. So changes to size and position of child have unexpected results.
    To avoid this, ensure child rect (a:chOff, a:chExt) is same as group rect (a:off, a:ext).
    See https://stackoverflow.com/a/56485145/100904
    '''
    if isinstance(shape, pptx.shapes.group.GroupShape):
        # Get group and child rect shapes
        xfrm = shape.element.find(qn('p:grpSpPr')).find(qn('a:xfrm'))
        off, ext = xfrm.find(qn('a:off')), xfrm.find(qn('a:ext'))
        choff, chext = xfrm.find(qn('a:chOff')), xfrm.find(qn('a:chExt'))
        # Calculate how much the child rect is transformed from the group rect
        tsx = sx * ext.cx / chext.cx
        tsy = sy * ext.cy / chext.cy
        tx = x + sx * (off.x - choff.x * ext.cx / chext.cx)
        ty = y + sy * (off.y - choff.y * ext.cy / chext.cy)
        # Transform child shapes using (x, y, sx, sy)
        for subshape in shape.shapes:
            flatten_group_transforms(subshape, tx, ty, tsx, tsy)
    # Transform shape positions using (x, y, sx, sy). But get all values before setting.
    # Placeholders need this: https://stackoverflow.com/a/49306814/100904
    left, top = int(shape.left * sx + x + 0.5), int(shape.top * sy + y + 0.5)
    width, height = int(shape.width * sx + 0.5), int(shape.height * sy + 0.5)
    shape.left, shape.top, shape.width, shape.height = left, top, width, height
    # Set child rect coords same as group rect (AFTER scaling the group)
    if isinstance(shape, pptx.shapes.group.GroupShape):
        choff.x, choff.y, chext.cx, chext.cy = off.x, off.y, ext.cx, ext.cy
Exemple #35
0
 def __getattr__(self, name):
     """
     Override ``__getattr__`` defined in ObjectifiedElement super class
     to intercept messages intended for custom property setters.
     """
     if name in ("b", "i"):
         return self._get_bool_attr(name)
     elif name == "eg_fillproperties":
         return self._eg_fillproperties()
     elif name == "hlinkClick":
         return self.find(qn("a:hlinkClick"))
     else:
         return super(CT_TextCharacterProperties, self).__getattr__(name)
Exemple #36
0
 def _get_date_prop(self, name):
     """Return datetime value of *name* property."""
     # explicit class reference avoids another pass through getattribute
     tag = qn(CT_CoreProperties._date_tags[name])
     # date properties return None when property element not present
     if not hasattr(self, tag):
         return None
     datetime_str = getattr(self, tag).text
     try:
         return self._parse_W3CDTF_to_datetime(datetime_str)
     except ValueError:
         # invalid datetime strings are ignored
         return None
 def test_has_table_return_value(self):
     """CT_GraphicalObjectFrame.has_table property has correct value"""
     # setup ------------------------
     id_, name = 9, 'Table 8'
     left, top, width, height = 111, 222, 333, 444
     tbl_uri = 'http://schemas.openxmlformats.org/drawingml/2006/table'
     chart_uri = 'http://schemas.openxmlformats.org/drawingml/2006/chart'
     graphicFrame = CT_GraphicalObjectFrame.new_graphicFrame(
         id_, name, left, top, width, height)
     graphicData = graphicFrame[qn('a:graphic')].graphicData
     # verify -----------------------
     graphicData.set('uri', tbl_uri)
     assert_that(graphicFrame.has_table, is_(equal_to(True)))
     graphicData.set('uri', chart_uri)
     assert_that(graphicFrame.has_table, is_(equal_to(False)))
Exemple #38
0
 def test_has_table_return_value(self):
     """CT_GraphicalObjectFrame.has_table property has correct value"""
     # setup ------------------------
     id_, name = 9, 'Table 8'
     left, top, width, height = 111, 222, 333, 444
     tbl_uri = 'http://schemas.openxmlformats.org/drawingml/2006/table'
     chart_uri = 'http://schemas.openxmlformats.org/drawingml/2006/chart'
     graphicFrame = CT_GraphicalObjectFrame.new_graphicFrame(
         id_, name, left, top, width, height)
     graphicData = graphicFrame[qn('a:graphic')].graphicData
     # verify -----------------------
     graphicData.set('uri', tbl_uri)
     assert_that(graphicFrame.has_table, is_(equal_to(True)))
     graphicData.set('uri', chart_uri)
     assert_that(graphicFrame.has_table, is_(equal_to(False)))
Exemple #39
0
 def _get_revision(self):
     """Return integer value of revision property."""
     tag = qn('cp:revision')
     # revision returns zero when element not present
     if not hasattr(self, tag):
         return 0
     revision_str = getattr(self, tag).text
     try:
         revision = int(revision_str)
     except ValueError:
         # non-integer revision strings also resolve to 0
         revision = 0
     # as do negative integers
     if revision < 0:
         revision = 0
     return revision
Exemple #40
0
    def new_table(id_, name, rows, cols, left, top, width, height):
        """
        Return a ``<p:graphicFrame>`` element tree populated with a table
        element.
        """
        graphicFrame = CT_GraphicalObjectFrame.new_graphicFrame(
            id_, name, left, top, width, height)

        # set type of contained graphic to table
        graphicData = graphicFrame[qn('a:graphic')].graphicData
        graphicData.set('uri', CT_GraphicalObjectFrame.DATATYPE_TABLE)

        # add tbl element tree
        tbl = CT_Table.new_tbl(rows, cols, width, height)
        graphicData.append(tbl)

        objectify.deannotate(graphicFrame, cleanup_namespaces=True)
        return graphicFrame
Exemple #41
0
    def recalculate_extents(self):
        """Adjust x, y, cx, and cy to incorporate all contained shapes.

        This would typically be called when a contained shape is added,
        removed, or its position or size updated.

        This method is recursive "upwards" since a change in a group shape
        can change the position and size of its containing group.
        """
        if not self.tag == qn('p:grpSp'):
            return

        x, y, cx, cy = self._child_extents

        self.chOff.x = self.x = x
        self.chOff.y = self.y = y
        self.chExt.cx = self.cx = cx
        self.chExt.cy = self.cy = cy
        self.getparent().recalculate_extents()
Exemple #42
0
    def recalculate_extents(self):
        """Adjust x, y, cx, and cy to incorporate all contained shapes.

        This would typically be called when a contained shape is added,
        removed, or its position or size updated.

        This method is recursive "upwards" since a change in a group shape
        can change the position and size of its containing group.
        """
        if not self.tag == qn("p:grpSp"):
            return

        x, y, cx, cy = self._child_extents

        self.chOff.x = self.x = x
        self.chOff.y = self.y = y
        self.chExt.cx = self.cx = cx
        self.chExt.cy = self.cy = cy
        self.getparent().recalculate_extents()
Exemple #43
0
def _SeriesFactory(ser):
    """
    Return an instance of the appropriate subclass of _BaseSeries based on the
    xChart element *ser* appears in.
    """
    xChart_tag = ser.getparent().tag

    try:
        SeriesCls = {
            qn("c:areaChart"): AreaSeries,
            qn("c:barChart"): BarSeries,
            qn("c:bubbleChart"): BubbleSeries,
            qn("c:doughnutChart"): PieSeries,
            qn("c:lineChart"): LineSeries,
            qn("c:pieChart"): PieSeries,
            qn("c:radarChart"): RadarSeries,
            qn("c:scatterChart"): XySeries,
        }[xChart_tag]
    except KeyError:
        raise NotImplementedError("series class for %s not yet implemented" %
                                  xChart_tag)

    return SeriesCls(ser)
Exemple #44
0
    def add_hlinkClick(self, rId):
        """
        Add an <a:hlinkClick> child element with r:id attribute set to *rId*.
        """
        assert self.find(qn("a:hlinkClick")) is None

        hlinkClick = Element("a:hlinkClick", nsmap("a", "r"))
        hlinkClick.set(qn("r:id"), rId)

        # find right insertion spot, will go away once xmlchemy comes in
        if self.find(qn("a:hlinkMouseOver")):
            successor = self.find(qn("a:hlinkMouseOver"))
            successor.addprevious(hlinkClick)
        elif self.find(qn("a:rtl")):
            successor = self.find(qn("a:rtl"))
            successor.addprevious(hlinkClick)
        elif self.find(qn("a:extLst")):
            successor = self.find(qn("a:extLst"))
            successor.addprevious(hlinkClick)
        else:
            self.append(hlinkClick)

        return hlinkClick
Exemple #45
0
def _SeriesFactory(ser):
    """
    Return an instance of the appropriate subclass of _BaseSeries based on the
    xChart element *ser* appears in.
    """
    xChart_tag = ser.getparent().tag

    try:
        SeriesCls = {
            qn("c:areaChart"): AreaSeries,
            qn("c:barChart"): BarSeries,
            qn("c:bubbleChart"): BubbleSeries,
            qn("c:doughnutChart"): PieSeries,
            qn("c:lineChart"): LineSeries,
            qn("c:pieChart"): PieSeries,
            qn("c:radarChart"): RadarSeries,
            qn("c:scatterChart"): XySeries,
        }[xChart_tag]
    except KeyError:
        raise NotImplementedError(
            "series class for %s not yet implemented" % xChart_tag
        )

    return SeriesCls(ser)
 def it_can_add_a_lumMod_child_element(self, schemeClr,
                                       schemeClr_with_lumMod_xml):
     lumMod = schemeClr.add_lumMod(0.75)
     assert schemeClr.xml == schemeClr_with_lumMod_xml
     assert schemeClr.find(qn("a:lumMod")) == lumMod
Exemple #47
0
 def it_can_add_a_lumMod_child_element(
         self, schemeClr, schemeClr_with_lumMod_xml):
     lumMod = schemeClr.add_lumMod(75000)
     assert schemeClr.xml == schemeClr_with_lumMod_xml
     assert schemeClr.find(qn('a:lumMod')) == lumMod
Exemple #48
0
 def it_can_add_a_lumOff_child_element(
         self, schemeClr, schemeClr_with_lumOff_xml):
     lumOff = schemeClr.add_lumOff(40000)
     assert schemeClr.xml == schemeClr_with_lumOff_xml
     assert schemeClr.find(qn('a:lumOff')) == lumOff
Exemple #49
0
 def bodyPr(self):
     return self[qn("a:bodyPr")]
 def iter_fixture(self, request):
     tbl_cxml = request.param
     tbl = element(tbl_cxml)
     rows = _RowCollection(tbl, None)
     expected_row_lst = tbl.findall(qn('a:tr'))
     return rows, expected_row_lst
Exemple #51
0
 def it_can_add_a_alpha_child_element(
         self, schemeClr, schemeClr_with_alpha_xml):
     alpha = schemeClr.add_alpha(0.4)
     assert schemeClr.xml == schemeClr_with_alpha_xml
     assert schemeClr.find(qn('a:alpha')) == alpha
Exemple #52
0
 def it_creates_a_new_child_if_one_is_not_present(self, parent_elm):
     child_elm = get_or_add(parent_elm, 'p:baz')
     assert child_elm.tag == qn('p:baz')
     assert child_elm.getparent() is parent_elm
Exemple #53
0
 def it_returns_an_element_with_the_specified_tag(self, nsptag_str):
     elm = Element(nsptag_str)
     assert elm.tag == qn(nsptag_str)
Exemple #54
0
def known_child_elm(parent_elm, known_child_nsptag_str):
    return parent_elm[qn(known_child_nsptag_str)]
Exemple #55
0
 def it_calculates_the_clark_name_for_an_ns_prefixed_tag_string(
         self, nsptag_str, clark_name):
     assert qn(nsptag_str) == clark_name
Exemple #56
0
    def iter_xCharts(self):
        """
        Generate each xChart child element in document.
        """
        plot_tags = (
            qn("c:area3DChart"),
            qn("c:areaChart"),
            qn("c:bar3DChart"),
            qn("c:barChart"),
            qn("c:bubbleChart"),
            qn("c:doughnutChart"),
            qn("c:line3DChart"),
            qn("c:lineChart"),
            qn("c:ofPieChart"),
            qn("c:pie3DChart"),
            qn("c:pieChart"),
            qn("c:radarChart"),
            qn("c:scatterChart"),
            qn("c:stockChart"),
            qn("c:surface3DChart"),
            qn("c:surfaceChart"),
        )

        for child in self.iterchildren():
            if child.tag not in plot_tags:
                continue
            yield child
Exemple #57
0
class CT_GroupShape(BaseShapeElement):
    """
    Used for the shape tree (``<p:spTree>``) element as well as the group
    shape (``<p:grpSp>``) element.
    """
    nvGrpSpPr = OneAndOnlyOne('p:nvGrpSpPr')
    grpSpPr = OneAndOnlyOne('p:grpSpPr')

    _shape_tags = (qn('p:sp'), qn('p:grpSp'), qn('p:graphicFrame'),
                   qn('p:cxnSp'), qn('p:pic'), qn('p:contentPart'))

    def add_autoshape(self, id_, name, prst, x, y, cx, cy):
        """
        Append a new ``<p:sp>`` shape to the group/shapetree having the
        properties specified in call.
        """
        sp = CT_Shape.new_autoshape_sp(id_, name, prst, x, y, cx, cy)
        self.insert_element_before(sp, 'p:extLst')
        return sp

    def add_cxnSp(self, id_, name, type_member, x, y, cx, cy, flipH, flipV):
        """
        Append a new ``<p:cxnSp>`` shape to the group/shapetree having the
        properties specified in call.
        """
        prst = MSO_CONNECTOR_TYPE.to_xml(type_member)
        cxnSp = CT_Connector.new_cxnSp(id_, name, prst, x, y, cx, cy, flipH,
                                       flipV)
        self.insert_element_before(cxnSp, 'p:extLst')
        return cxnSp

    def add_freeform_sp(self, x, y, cx, cy):
        """Append a new freeform `p:sp` with specified position and size."""
        shape_id = self._next_shape_id
        name = 'Freeform %d' % (shape_id - 1, )
        sp = CT_Shape.new_freeform_sp(shape_id, name, x, y, cx, cy)
        self.insert_element_before(sp, 'p:extLst')
        return sp

    def add_grpSp(self):
        """Return `p:grpSp` element newly appended to this shape tree.

        The element contains no sub-shapes, is positioned at (0, 0), and has
        width and height of zero.
        """
        shape_id = self._next_shape_id
        name = 'Group %d' % (shape_id - 1, )
        grpSp = CT_GroupShape.new_grpSp(shape_id, name)
        self.insert_element_before(grpSp, 'p:extLst')
        return grpSp

    def add_pic(self, id_, name, desc, rId, x, y, cx, cy):
        """
        Append a ``<p:pic>`` shape to the group/shapetree having properties
        as specified in call.
        """
        pic = CT_Picture.new_pic(id_, name, desc, rId, x, y, cx, cy)
        self.insert_element_before(pic, 'p:extLst')
        return pic

    def add_placeholder(self, id_, name, ph_type, orient, sz, idx):
        """
        Append a newly-created placeholder ``<p:sp>`` shape having the
        specified placeholder properties.
        """
        sp = CT_Shape.new_placeholder_sp(id_, name, ph_type, orient, sz, idx)
        self.insert_element_before(sp, 'p:extLst')
        return sp

    def add_table(self, id_, name, rows, cols, x, y, cx, cy):
        """
        Append a ``<p:graphicFrame>`` shape containing a table as specified
        in call.
        """
        graphicFrame = CT_GraphicalObjectFrame.new_table_graphicFrame(
            id_, name, rows, cols, x, y, cx, cy)
        self.insert_element_before(graphicFrame, 'p:extLst')
        return graphicFrame

    def add_textbox(self, id_, name, x, y, cx, cy):
        """
        Append a newly-created textbox ``<p:sp>`` shape having the specified
        position and size.
        """
        sp = CT_Shape.new_textbox_sp(id_, name, x, y, cx, cy)
        self.insert_element_before(sp, 'p:extLst')
        return sp

    @property
    def chExt(self):
        """Descendent `p:grpSpPr/a:xfrm/a:chExt` element."""
        return self.grpSpPr.get_or_add_xfrm().get_or_add_chExt()

    @property
    def chOff(self):
        """Descendent `p:grpSpPr/a:xfrm/a:chOff` element."""
        return self.grpSpPr.get_or_add_xfrm().get_or_add_chOff()

    def get_or_add_xfrm(self):
        """
        Return the ``<a:xfrm>`` grandchild element, newly-added if not
        present.
        """
        return self.grpSpPr.get_or_add_xfrm()

    def iter_ph_elms(self):
        """
        Generate each placeholder shape child element in document order.
        """
        for e in self.iter_shape_elms():
            if e.has_ph_elm:
                yield e

    def iter_shape_elms(self):
        """
        Generate each child of this ``<p:spTree>`` element that corresponds
        to a shape, in the sequence they appear in the XML.
        """
        for elm in self.iterchildren():
            if elm.tag in self._shape_tags:
                yield elm

    @classmethod
    def new_grpSp(cls, id_, name):
        """Return new "loose" `p:grpSp` element having *id_* and *name*."""
        xml = ('<p:grpSp %s>\n'
               '  <p:nvGrpSpPr>\n'
               '    <p:cNvPr id="%%d" name="%%s"/>\n'
               '    <p:cNvGrpSpPr/>\n'
               '    <p:nvPr/>\n'
               '  </p:nvGrpSpPr>\n'
               '  <p:grpSpPr>\n'
               '    <a:xfrm>\n'
               '      <a:off x="0" y="0"/>\n'
               '      <a:ext cx="0" cy="0"/>\n'
               '      <a:chOff x="0" y="0"/>\n'
               '      <a:chExt cx="0" cy="0"/>\n'
               '    </a:xfrm>\n'
               '  </p:grpSpPr>\n'
               '</p:grpSp>' % nsdecls('a', 'p', 'r')) % (id_, name)
        grpSp = parse_xml(xml)
        return grpSp

    def recalculate_extents(self):
        """Adjust x, y, cx, and cy to incorporate all contained shapes.

        This would typically be called when a contained shape is added,
        removed, or its position or size updated.

        This method is recursive "upwards" since a change in a group shape
        can change the position and size of its containing group.
        """
        if not self.tag == qn('p:grpSp'):
            return

        x, y, cx, cy = self._child_extents

        self.chOff.x = self.x = x
        self.chOff.y = self.y = y
        self.chExt.cx = self.cx = cx
        self.chExt.cy = self.cy = cy
        self.getparent().recalculate_extents()

    @property
    def xfrm(self):
        """
        The ``<a:xfrm>`` grandchild element or |None| if not found
        """
        return self.grpSpPr.xfrm

    @property
    def _child_extents(self):
        """(x, y, cx, cy) tuple representing net position and size.

        The values are formed as a composite of the contained child shapes.
        """
        child_shape_elms = list(self.iter_shape_elms())

        if not child_shape_elms:
            return Emu(0), Emu(0), Emu(0), Emu(0)

        min_x = min([xSp.x for xSp in child_shape_elms])
        min_y = min([xSp.y for xSp in child_shape_elms])
        max_x = max([(xSp.x + xSp.cx) for xSp in child_shape_elms])
        max_y = max([(xSp.y + xSp.cy) for xSp in child_shape_elms])

        x = min_x
        y = min_y
        cx = max_x - min_x
        cy = max_y - min_y

        return x, y, cx, cy

    @property
    def _next_shape_id(self):
        """Return unique shape id suitable for use with a new shape element.

        The returned id is the next available positive integer drawing object
        id in shape tree, starting from 1 and making use of any gaps in
        numbering. In practice, the minimum id is 2 because the spTree
        element itself is always assigned id="1".
        """
        id_str_lst = self.xpath('//@id')
        used_ids = [int(id_str) for id_str in id_str_lst if id_str.isdigit()]
        for n in range(1, len(used_ids) + 2):
            if n not in used_ids:
                return n
Exemple #58
0
 def iter_fixture(self, request):
     tr_cxml = request.param
     tr = element(tr_cxml)
     cells = _CellCollection(tr, None)
     expected_cell_lst = tr.findall(qn('a:tc'))
     return cells, expected_cell_lst