'Cleans the document.'

def __call__(self, doc):

    if hasattr(doc, 'getroot'):
        doc = doc.getroot()
    xhtml_to_html(doc)
    for el in doc.iter('image'):
        el.tag = 'img'
    if (not self.comments):
        self.kill_conditional_comments(doc)
    kill_tags = set((self.kill_tags or ()))
    remove_tags = set((self.remove_tags or ()))
    allow_tags = set((self.allow_tags or ()))
    if self.scripts:
        kill_tags.add('script')
    if self.safe_attrs_only:
        safe_attrs = set(self.safe_attrs)
        for el in doc.iter():
            attrib = el.attrib
            for aname in attrib.keys():
                if (aname not in safe_attrs):
                    del attrib[aname]
    if self.javascript:
        if (not (self.safe_attrs_only and (self.safe_attrs == defs.safe_attrs))):
            for el in doc.iter():
                attrib = el.attrib
                for aname in attrib.keys():
                    if aname.startswith('on'):
                        del attrib[aname]
        doc.rewrite_links(self._remove_javascript_link, resolve_base_href=False)
        if (not self.style):
            for el in _find_styled_elements(doc):
                old = el.get('style')
                new = _css_javascript_re.sub('', old)
                new = _css_import_re.sub('', new)
                if self._has_sneaky_javascript(new):
                    del el.attrib['style']
                elif (new != old):
                    el.set('style', new)
            for el in list(doc.iter('style')):
                if (el.get('type', '').lower().strip() == 'text/javascript'):
                    el.drop_tree()
                    continue
                old = (el.text or '')
                new = _css_javascript_re.sub('', old)
                new = _css_import_re.sub('', old)
                if self._has_sneaky_javascript(new):
                    el.text = '/*    deleted    */'
                elif (new != old):
                    el.text = new
    if (self.comments or self.processing_instructions):
        kill_tags.add(etree.Comment)
    if self.processing_instructions:
        kill_tags.add(etree.ProcessingInstruction)
    if self.style:
        kill_tags.add('style')
        etree.strip_attributes(doc, 'style')
    if self.links:
        kill_tags.add('link')
    elif (self.style or self.javascript):
        for el in list(doc.iter('link')):
            if ('stylesheet' in el.get('rel', '').lower()):
                if (not self.allow_element(el)):
                    el.drop_tree()
    if self.meta:
        kill_tags.add('meta')
    if self.page_structure:
        remove_tags.update(('head', 'html', 'title'))
    if self.embedded:
        for el in list(doc.iter('param')):
            found_parent = False
            parent = el.getparent()
            while ((parent is not None) and (parent.tag not in ('applet', 'object'))):
                parent = parent.getparent()
            if (parent is None):
                el.drop_tree()
        kill_tags.update(('applet',))
        remove_tags.update(('iframe', 'embed', 'layer', 'object', 'param'))
    if self.frames:
        kill_tags.update(defs.frame_tags)
    if self.forms:
        remove_tags.add('form')
        kill_tags.update(('button', 'input', 'select', 'textarea'))
    if self.annoying_tags:
        remove_tags.update(('blink', 'marquee'))
    _remove = []
    _kill = []
    for el in doc.iter():
        if (el.tag in kill_tags):
            if self.allow_element(el):
                continue
            _kill.append(el)
        elif (el.tag in remove_tags):
            if self.allow_element(el):
                continue
            _remove.append(el)
    if (_remove and (_remove[0] == doc)):
        el = _remove.pop(0)
        el.tag = 'div'
        el.attrib.clear()
    elif (_kill and (_kill[0] == doc)):
        el = _kill.pop(0)
        if (el.tag != 'html'):
            el.tag = 'div'
        el.clear()
    _kill.reverse()
    for el in _kill:
        el.drop_tree()
    for el in _remove:
        el.drop_tag()
    if self.remove_unknown_tags:
        if allow_tags:
            raise ValueError('It    does    not    make    sense    to    pass    in    both    allow_tags    and    remove_unknown_tags')
        allow_tags = set(defs.tags)
    if allow_tags:
        bad = []
        for el in doc.iter():
            if (el.tag not in allow_tags):
                bad.append(el)
        if bad:
            if (bad[0] is doc):
                el = bad.pop(0)
                el.tag = 'div'
                el.attrib.clear()
            for el in bad:
                el.drop_tag()
    if self.add_nofollow:
        for el in _find_external_links(doc):
            if (not self.allow_follow(el)):
                rel = el.get('rel')
                if rel:
                    if (('nofollow' in rel) and ('    nofollow    ' in ('    %s    ' % rel))):
                        continue
                    rel = ('%s    nofollow' % rel)
                else:
                    rel = 'nofollow'
                el.set('rel', rel)
