def rename(self, current_name, new_name): ''' Renames a file from current_name to new_name. It automatically rebases all links inside the file if the directory the file is in changes. Note however, that links are not updated in the other files that could reference this file. This is for performance, such updates should be done once, in bulk. ''' if current_name in self.names_that_must_not_be_changed: raise ValueError('Renaming of %s is not allowed' % current_name) if self.exists(new_name) and ( new_name == current_name or new_name.lower() != current_name.lower()): # The destination exists and does not differ from the current name only by case raise ValueError('Cannot rename %s to %s as %s already exists' % (current_name, new_name, new_name)) new_path = self.name_to_abspath(new_name) base = os.path.dirname(new_path) if os.path.isfile(base): raise ValueError('Cannot rename %s to %s as %s is a file' % (current_name, new_name, base)) if not os.path.exists(base): os.makedirs(base) old_path = parent_dir = self.name_to_abspath(current_name) self.commit_item(current_name) os.rename(old_path, new_path) # Remove empty directories while parent_dir: parent_dir = os.path.dirname(parent_dir) try: os.rmdir(parent_dir) except EnvironmentError: break for x in ('mime_map', 'encoding_map'): x = getattr(self, x) if current_name in x: x[new_name] = x[current_name] self.name_path_map[new_name] = new_path for x in self.cache_names: x = getattr(self, x) try: x.pop(current_name, None) except TypeError: x.discard(current_name) if current_name == self.opf_name: self.opf_name = new_name if os.path.dirname(old_path) != os.path.dirname(new_path): from calibre.ebooks.oeb.polish.replace import LinkRebaser repl = LinkRebaser(self, current_name, new_name) self.replace_links(new_name, repl) self.dirty(new_name)
def merge_css(container, names, master): p = container.parsed msheet = p(master) master_base = os.path.dirname(master) merged = set() for name in names: if name == master: continue # Rebase links if master is in a different directory if os.path.dirname(name) != master_base: container.replace_links(name, LinkRebaser(container, name, master)) sheet = p(name) # Remove charset rules cr = [r for r in sheet.cssRules if r.type == r.CHARSET_RULE] [sheet.deleteRule(sheet.cssRules.index(r)) for r in cr] for rule in sheet.cssRules: msheet.add(rule) container.remove_item(name) merged.add(name) # Remove links to merged stylesheets in the html files, replacing with a # link to the master sheet for name, mt in iteritems(container.mime_map): if mt in OEB_DOCS: removed = False root = p(name) for link in XPath('//h:link[@href]')(root): q = container.href_to_name(link.get('href'), name) if q in merged: container.remove_from_xml(link) removed = True if removed: container.dirty(name) if removed and master not in set(all_stylesheets(container, name)): head = root.find('h:head', namespaces=XPNSMAP) if head is not None: link = head.makeelement(XHTML('link'), type='text/css', rel='stylesheet', href=container.name_to_href( master, name)) container.insert_into_xml(head, link)
def merge_html(container, names, master): p = container.parsed root = p(master) # Ensure master has a <head> head = root.find('h:head', namespaces=XPNSMAP) if head is None: head = root.makeelement(XHTML('head')) container.insert_into_xml(root, head, 0) seen_anchors = all_anchors(root) seen_stylesheets = set(all_stylesheets(container, master)) master_body = p(master).findall('h:body', namespaces=XPNSMAP)[-1] master_base = os.path.dirname(master) anchor_map = {n: {} for n in names if n != master} for name in names: if name == master: continue # Insert new stylesheets into master for sheet in all_stylesheets(container, name): if sheet not in seen_stylesheets: seen_stylesheets.add(sheet) link = head.makeelement(XHTML('link'), rel='stylesheet', type='text/css', href=container.name_to_href( sheet, master)) container.insert_into_xml(head, link) # Rebase links if master is in a different directory if os.path.dirname(name) != master_base: container.replace_links(name, LinkRebaser(container, name, master)) root = p(name) children = [] for body in p(name).findall('h:body', namespaces=XPNSMAP): children.append( body.text if body.text and body.text.strip() else '\n\n') children.extend(body) first_child = '' for first_child in children: if not isinstance(first_child, string_or_bytes): break if isinstance(first_child, string_or_bytes): # body contained only text, no tags first_child = body.makeelement(XHTML('p')) first_child.text, children[0] = children[0], first_child amap = anchor_map[name] remove_name_attributes(root) for elem in root.xpath('//*[@id]'): val = elem.get('id') if not val: continue if val in seen_anchors: nval = unique_anchor(seen_anchors, val) elem.set('id', nval) amap[val] = nval else: seen_anchors.add(val) if 'id' not in first_child.attrib: first_child.set('id', unique_anchor(seen_anchors, 'top')) seen_anchors.add(first_child.get('id')) amap[''] = first_child.get('id') # Fix links that point to local changed anchors for a in XPath('//h:a[starts-with(@href, "#")]')(root): q = a.get('href')[1:] if q in amap: a.set('href', '#' + amap[q]) for child in children: if isinstance(child, string_or_bytes): add_text(master_body, child) else: master_body.append(copy.deepcopy(child)) container.remove_item(name, remove_from_guide=False) # Fix all links in the container that point to merged files for fname, media_type in iteritems(container.mime_map): repl = MergeLinkReplacer(fname, anchor_map, master, container) container.replace_links(fname, repl)