diff --git a/resources/recipes/alternet.recipe b/resources/recipes/alternet.recipe new file mode 100644 index 0000000000..d9498c7a0c --- /dev/null +++ b/resources/recipes/alternet.recipe @@ -0,0 +1,38 @@ +from calibre.ptempfile import PersistentTemporaryFile +from calibre.web.feeds.news import BasicNewsRecipe + +class Alternet(BasicNewsRecipe): + title = u'Alternet' + __author__= 'rty' + oldest_article = 7 + max_articles_per_feed = 100 + publisher = 'alternet.org' + category = 'News, Magazine' + description = 'News magazine and online community' + feeds = [ + (u'Front Page', u'http://feeds.feedblitz.com/alternet'), + (u'Breaking News', u'http://feeds.feedblitz.com/alternet_breaking_news'), + (u'Top Ten Campaigns', u'http://feeds.feedblitz.com/alternet_top_10_campaigns'), + (u'Special Coverage Areas', u'http://feeds.feedblitz.com/alternet_coverage') + ] + + remove_javascript = True + use_embedded_content = False + no_stylesheets = True + language = 'en' + encoding = 'UTF-8' + temp_files = [] + articles_are_obfuscated = True + + def get_article_url(self, article): + return article.get('link', None) + + def get_obfuscated_article(self, url): + br = self.get_browser() + br.open(url) + response = br.follow_link(url_regex = r'/printversion/[0-9]+', nr = 0) + html = response.read() + self.temp_files.append(PersistentTemporaryFile('_fa.html')) + self.temp_files[-1].write(html) + self.temp_files[-1].close() + return self.temp_files[-1].name diff --git a/resources/templates/rtf.xsl b/resources/templates/rtf.xsl index 74696f0857..c3162b0c15 100644 --- a/resources/templates/rtf.xsl +++ b/resources/templates/rtf.xsl @@ -265,9 +265,20 @@ pt; + + text-align: justify; + + + text-align: center; + + + text-align: left; + + + text-align: right; + - diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py index 92ee2ca6d2..15bd54c80c 100644 --- a/src/calibre/__init__.py +++ b/src/calibre/__init__.py @@ -361,6 +361,8 @@ def strftime(fmt, t=None): before 1900 ''' if t is None: t = time.localtime() + if hasattr(t, 'timetuple'): + t = t.timetuple() early_year = t[0] < 1900 if early_year: replacement = 1900 if t[0]%4 == 0 else 1901 diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 9d876b42d1..35cb0ad3d2 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -446,7 +446,7 @@ from calibre.devices.eb600.driver import EB600, COOL_ER, SHINEBOOK, \ BOOQ, ELONEX, POCKETBOOK301, MENTOR from calibre.devices.iliad.driver import ILIAD from calibre.devices.irexdr.driver import IREXDR1000, IREXDR800 -from calibre.devices.jetbook.driver import JETBOOK +from calibre.devices.jetbook.driver import JETBOOK, MIBUK from calibre.devices.kindle.driver import KINDLE, KINDLE2, KINDLE_DX from calibre.devices.nook.driver import NOOK from calibre.devices.prs505.driver import PRS505 @@ -467,12 +467,12 @@ from calibre.devices.kobo.driver import KOBO from calibre.ebooks.metadata.fetch import GoogleBooks, ISBNDB, Amazon, \ LibraryThing from calibre.ebooks.metadata.douban import DoubanBooks -from calibre.library.catalog import CSV_XML, EPUB_MOBI +from calibre.library.catalog import CSV_XML, EPUB_MOBI, BIBTEX from calibre.ebooks.epub.fix.unmanifested import Unmanifested from calibre.ebooks.epub.fix.epubcheck import Epubcheck plugins = [HTML2ZIP, PML2PMLZ, ArchiveExtract, GoogleBooks, ISBNDB, Amazon, - LibraryThing, DoubanBooks, CSV_XML, EPUB_MOBI, Unmanifested, Epubcheck] + LibraryThing, DoubanBooks, CSV_XML, EPUB_MOBI, BIBTEX, Unmanifested, Epubcheck] plugins += [ ComicInput, EPUBInput, @@ -517,6 +517,7 @@ plugins += [ IREXDR1000, IREXDR800, JETBOOK, + MIBUK, SHINEBOOK, POCKETBOOK360, POCKETBOOK301, diff --git a/src/calibre/devices/jetbook/driver.py b/src/calibre/devices/jetbook/driver.py index 671fea5d75..6a3bc635ff 100644 --- a/src/calibre/devices/jetbook/driver.py +++ b/src/calibre/devices/jetbook/driver.py @@ -80,3 +80,21 @@ class JETBOOK(USBMS): return mi +class MIBUK(USBMS): + + name = 'MiBuk Wolder Device Interface' + description = _('Communicate with the MiBuk Wolder reader.') + author = 'Kovid Goyal' + supported_platforms = ['windows', 'osx', 'linux'] + + FORMATS = ['epub', 'mobi', 'prc', 'fb2', 'txt', 'rtf', 'pdf'] + + VENDOR_ID = [0x0525] + PRODUCT_ID = [0xa4a5] + BCD = [0x314] + SUPPORTS_SUB_DIRS = True + + VENDOR_NAME = 'LINUX' + WINDOWS_MAIN_MEM = 'WOLDERMIBUK' + + diff --git a/src/calibre/ebooks/rtf/input.py b/src/calibre/ebooks/rtf/input.py index d5e1a95157..50f5571d58 100644 --- a/src/calibre/ebooks/rtf/input.py +++ b/src/calibre/ebooks/rtf/input.py @@ -192,12 +192,18 @@ class RTFInput(InputFormatPlugin): from calibre.ebooks.rtf2xml.ParseRtf import RtfInvalidCodeException self.log = log self.log('Converting RTF to XML...') + #Name of the preprocesssed RTF file fname = self.preprocess(stream.name) try: xml = self.generate_xml(fname) except RtfInvalidCodeException, e: raise ValueError(_('This RTF file has a feature calibre does not ' 'support. Convert it to HTML first and then try it.\n%s')%e) + + '''dataxml = open('dataxml.xml', 'w') + dataxml.write(xml) + dataxml.close''' + d = glob.glob(os.path.join('*_rtf_pict_dir', 'picts.rtf')) if d: imap = {} @@ -205,6 +211,7 @@ class RTFInput(InputFormatPlugin): imap = self.extract_images(d[0]) except: self.log.exception('Failed to extract images...') + self.log('Parsing XML...') parser = etree.XMLParser(recover=True, no_network=True) doc = etree.fromstring(xml, parser=parser) @@ -214,10 +221,10 @@ class RTFInput(InputFormatPlugin): name = imap.get(num, None) if name is not None: pict.set('num', name) + self.log('Converting XML to HTML...') inline_class = InlineClass(self.log) styledoc = etree.fromstring(P('templates/rtf.xsl', data=True)) - extensions = { ('calibre', 'inline-class') : inline_class } transform = etree.XSLT(styledoc, extensions=extensions) result = transform(doc) diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index d922af0914..41d72d17f1 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -4,7 +4,7 @@ __copyright__ = '2008, Kovid Goyal ' import os, sys from threading import RLock -from PyQt4.Qt import QVariant, QFileInfo, QObject, SIGNAL, QBuffer, Qt, QSize, \ +from PyQt4.Qt import QVariant, QFileInfo, QObject, SIGNAL, QBuffer, Qt, \ QByteArray, QTranslator, QCoreApplication, QThread, \ QEvent, QTimer, pyqtSignal, QDate, QDesktopServices, \ QFileDialog, QMessageBox, QPixmap, QFileIconProvider, \ @@ -33,10 +33,6 @@ def _config(): help=_('Send file to storage card instead of main memory by default')) c.add_opt('confirm_delete', default=False, help=_('Confirm before deleting')) - c.add_opt('toolbar_icon_size', default=QSize(48, 48), - help=_('Toolbar icon size')) # value QVariant.toSize - c.add_opt('show_text_in_toolbar', default=True, - help=_('Show button labels in the toolbar')) c.add_opt('main_window_geometry', default=None, help=_('Main window geometry')) # value QVariant.toByteArray c.add_opt('new_version_notification', default=True, diff --git a/src/calibre/gui2/catalog/catalog_bibtex.py b/src/calibre/gui2/catalog/catalog_bibtex.py new file mode 100644 index 0000000000..ea222603b7 --- /dev/null +++ b/src/calibre/gui2/catalog/catalog_bibtex.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai +from __future__ import with_statement + +__license__ = 'GPL v3' +__copyright__ = '2009, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + + +from calibre.gui2 import gprefs +from calibre.gui2.catalog.catalog_bibtex_ui import Ui_Form +from PyQt4.Qt import QWidget, QListWidgetItem + +class PluginWidget(QWidget, Ui_Form): + + TITLE = _('BibTeX Options') + HELP = _('Options specific to')+' BibTeX '+_('output') + OPTION_FIELDS = [('bib_cit','{authors}{id}'), + ('bib_entry', 0), #mixed + ('bibfile_enc', 0), #utf-8 + ('bibfile_enctag', 0), #strict + ('impcit', True) ] + + sync_enabled = False + formats = set(['bib']) + + def __init__(self, parent=None): + QWidget.__init__(self, parent) + self.setupUi(self) + from calibre.library.catalog import FIELDS + self.all_fields = [] + for x in FIELDS : + if x != 'all': + self.all_fields.append(x) + QListWidgetItem(x, self.db_fields) + + def initialize(self, name): #not working properly to update + self.name = name + fields = gprefs.get(name+'_db_fields', self.all_fields) + # Restore the activated db_fields from last use + for x in xrange(self.db_fields.count()): + item = self.db_fields.item(x) + item.setSelected(unicode(item.text()) in fields) + # Update dialog fields from stored options + for opt in self.OPTION_FIELDS: + opt_value = gprefs.get(self.name + '_' + opt[0], opt[1]) + if opt[0] in ['bibfile_enc', 'bibfile_enctag', 'bib_entry']: + getattr(self, opt[0]).setCurrentIndex(opt_value) + elif opt[0] == 'impcit' : + getattr(self, opt[0]).setChecked(opt_value) + else: + getattr(self, opt[0]).setText(opt_value) + + def options(self): + + # Save the currently activated fields + fields = [] + for x in xrange(self.db_fields.count()): + item = self.db_fields.item(x) + if item.isSelected(): + fields.append(unicode(item.text())) + gprefs.set(self.name+'_db_fields', fields) + + # Dictionary currently activated fields + if len(self.db_fields.selectedItems()): + opts_dict = {'fields':[unicode(item.text()) for item in self.db_fields.selectedItems()]} + else: + opts_dict = {'fields':['all']} + + # Save/return the current options + # bib_cit stores as text + # 'bibfile_enc','bibfile_enctag' stores as int (Indexes) + for opt in self.OPTION_FIELDS: + if opt[0] in ['bibfile_enc', 'bibfile_enctag', 'bib_entry']: + opt_value = getattr(self,opt[0]).currentIndex() + elif opt[0] == 'impcit' : + opt_value = getattr(self, opt[0]).isChecked() + else : + opt_value = unicode(getattr(self, opt[0]).text()) + gprefs.set(self.name + '_' + opt[0], opt_value) + + opts_dict[opt[0]] = opt_value + + return opts_dict diff --git a/src/calibre/gui2/catalog/catalog_bibtex.ui b/src/calibre/gui2/catalog/catalog_bibtex.ui new file mode 100644 index 0000000000..7f4920655d --- /dev/null +++ b/src/calibre/gui2/catalog/catalog_bibtex.ui @@ -0,0 +1,173 @@ + + + Form + + + + 0 + 0 + 579 + 411 + + + + Form + + + + + + Bib file encoding: + + + + + + + Fields to include in output: + + + + + + + + utf-8 + + + + + cp1252 + + + + + ascii/LaTeX + + + + + + + + + 0 + 0 + + + + + + + QAbstractItemView::MultiSelection + + + + + + + Encoding configuration (change if you have errors) : + + + + + + + + strict + + + + + replace + + + + + ignore + + + + + backslashreplace + + + + + + + + Qt::Vertical + + + + 20 + 60 + + + + + + + + BibTeX entry type: + + + + + + + + mixed + + + + + misc + + + + + book + + + + + + + + Create a citation tag? + + + + + + + Expression to form the BibTeX citation tag: + + + + + + + + + + Some explanation about this template: + -The fields availables are 'author_sort', 'authors', 'id', + 'isbn', 'pubdate', 'publisher', 'series_index', 'series', + 'tags', 'timestamp', 'title', 'uuid' + -For list types ie authors and tags, only the first element + wil be selected. + -For time field, only the date will be used. + + + false + + + + + + + + diff --git a/src/calibre/gui2/convert/metadata.ui b/src/calibre/gui2/convert/metadata.ui index ec5a913f18..7bc45e234e 100644 --- a/src/calibre/gui2/convert/metadata.ui +++ b/src/calibre/gui2/convert/metadata.ui @@ -20,6 +20,30 @@ Book Cover + + + + + + + 0 + 0 + + + + + + + + + + Use cover from &source file + + + true + + + @@ -71,30 +95,6 @@ - - - - Use cover from &source file - - - true - - - - - - - - - - 0 - 0 - - - - - - opt_prefer_metadata_cover @@ -232,9 +232,6 @@ QComboBox::InsertAlphabetically - - QComboBox::AdjustToContents - diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index bc8ba7c381..c919547956 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -638,7 +638,6 @@ class DeviceMixin(object): # {{{ self.device_error_dialog = error_dialog(self, _('Error'), _('Error communicating with device'), ' ') self.device_error_dialog.setModal(Qt.NonModal) - self.device_connected = None self.emailer = Emailer() self.emailer.start() self.device_manager = DeviceManager(Dispatcher(self.device_detected), @@ -755,17 +754,14 @@ class DeviceMixin(object): # {{{ self.device_manager.device.__class__.get_gui_name()+\ _(' detected.'), 3000) self.device_connected = device_kind - self.location_view.model().device_connected(self.device_manager.device) self.refresh_ondevice_info (device_connected = True, reset_only = True) else: self.device_connected = None self.status_bar.device_disconnected() - self.location_view.model().update_devices() if self.current_view() != self.library_view: self.book_details.reset_info() - self.location_view.setCurrentIndex(self.location_view.model().index(0)) - self.refresh_ondevice_info (device_connected = False) - self.tool_bar.device_status_changed(bool(connected)) + self.location_manager.update_devices() + self.refresh_ondevice_info(device_connected=False) def info_read(self, job): ''' @@ -774,7 +770,8 @@ class DeviceMixin(object): # {{{ if job.failed: return self.device_job_exception(job) info, cp, fs = job.result - self.location_view.model().update_devices(cp, fs) + self.location_manager.update_devices(cp, fs, + self.device_manager.device.icon) self.status_bar.device_connected(info[0]) self.device_manager.books(Dispatcher(self.metadata_downloaded)) @@ -1076,9 +1073,9 @@ class DeviceMixin(object): # {{{ dynamic.set('catalogs_to_be_synced', set([])) if files: remove = [] - space = { self.location_view.model().free[0] : None, - self.location_view.model().free[1] : 'carda', - self.location_view.model().free[2] : 'cardb' } + space = { self.location_manager.free[0] : None, + self.location_manager.free[1] : 'carda', + self.location_manager.free[2] : 'cardb' } on_card = space.get(sorted(space.keys(), reverse=True)[0], None) self.upload_books(files, names, metadata, on_card=on_card, @@ -1140,9 +1137,9 @@ class DeviceMixin(object): # {{{ dynamic.set('news_to_be_synced', set([])) if config['upload_news_to_device'] and files: remove = ids if del_on_upload else [] - space = { self.location_view.model().free[0] : None, - self.location_view.model().free[1] : 'carda', - self.location_view.model().free[2] : 'cardb' } + space = { self.location_manager.free[0] : None, + self.location_manager.free[1] : 'carda', + self.location_manager.free[2] : 'cardb' } on_card = space.get(sorted(space.keys(), reverse=True)[0], None) self.upload_books(files, names, metadata, on_card=on_card, @@ -1263,7 +1260,8 @@ class DeviceMixin(object): # {{{ self.device_job_exception(job) return cp, fs = job.result - self.location_view.model().update_devices(cp, fs) + self.location_manager.update_devices(cp, fs, + self.device_manager.device.icon) # reset the views so that up-to-date info is shown. These need to be # here because the sony driver updates collections in sync_booklists self.memory_view.reset() diff --git a/src/calibre/gui2/dialogs/choose_library.py b/src/calibre/gui2/dialogs/choose_library.py new file mode 100644 index 0000000000..d656a93b6d --- /dev/null +++ b/src/calibre/gui2/dialogs/choose_library.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai + +__license__ = 'GPL v3' +__copyright__ = '2010, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +import os + +from PyQt4.Qt import QDialog + +from calibre.gui2.dialogs.choose_library_ui import Ui_Dialog +from calibre.gui2 import error_dialog, choose_dir +from calibre.constants import filesystem_encoding +from calibre import isbytestring, patheq +from calibre.utils.config import prefs +from calibre.gui2.wizard import move_library + +class ChooseLibrary(QDialog, Ui_Dialog): + + def __init__(self, db, callback, parent): + QDialog.__init__(self, parent) + self.setupUi(self) + self.db = db + self.new_db = None + self.callback = callback + + lp = db.library_path + if isbytestring(lp): + lp = lp.decode(filesystem_encoding) + loc = unicode(self.old_location.text()).format(lp) + self.old_location.setText(loc) + self.browse_button.clicked.connect(self.choose_loc) + + def choose_loc(self, *args): + loc = choose_dir(self, 'choose library location', + _('Choose location for calibre library')) + if loc is not None: + self.location.setText(loc) + + def check_action(self, ac, loc): + exists = self.db.exists_at(loc) + if patheq(loc, self.db.library_path): + error_dialog(self, _('Same as current'), + _('The location %s contains the current calibre' + ' library')%loc, show=True) + return False + empty = not os.listdir(loc) + if ac == 'existing' and not exists: + error_dialog(self, _('No existing library found'), + _('There is no existing calibre library at %s')%loc, + show=True) + return False + if ac in ('new', 'move') and not empty: + error_dialog(self, _('Not empty'), + _('The folder %s is not empty. Please choose an empty' + ' folder')%loc, + show=True) + return False + + return True + + def perform_action(self, ac, loc): + if ac in ('new', 'existing'): + prefs['library_path'] = loc + self.callback(loc) + else: + move_library(self.db.library_path, loc, self.parent(), + self.callback) + + def accept(self): + action = 'move' + if self.existing_library.isChecked(): + action = 'existing' + elif self.empty_library.isChecked(): + action = 'new' + loc = os.path.abspath(unicode(self.location.text()).strip()) + if not loc or not os.path.exists(loc) or not self.check_action(action, + loc): + return + QDialog.accept(self) + self.perform_action(action, loc) diff --git a/src/calibre/gui2/dialogs/choose_library.ui b/src/calibre/gui2/dialogs/choose_library.ui new file mode 100644 index 0000000000..793c805eda --- /dev/null +++ b/src/calibre/gui2/dialogs/choose_library.ui @@ -0,0 +1,171 @@ + + + Dialog + + + + 0 + 0 + 602 + 245 + + + + Choose your calibre library + + + + :/images/lt.png:/images/lt.png + + + + + + Your calibre library is currently located at {0} + + + true + + + + + + + New &Location: + + + location + + + + + + + true + + + + + + + Use &existing library at the new location + + + true + + + + + + + &Create an empty library at the new location + + + + + + + &Move current library to new location + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + ... + + + + :/images/document_open.svg:/images/document_open.svg + + + + + + + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/calibre/gui2/dialogs/config/__init__.py b/src/calibre/gui2/dialogs/config/__init__.py index b064dc53c2..84d55c8fb6 100644 --- a/src/calibre/gui2/dialogs/config/__init__.py +++ b/src/calibre/gui2/dialogs/config/__init__.py @@ -14,7 +14,7 @@ from PyQt4.Qt import QDialog, QListWidgetItem, QIcon, \ from calibre.constants import iswindows, isosx from calibre.gui2.dialogs.config.config_ui import Ui_Dialog from calibre.gui2.dialogs.config.create_custom_column import CreateCustomColumn -from calibre.gui2 import choose_dir, error_dialog, config, gprefs, \ +from calibre.gui2 import error_dialog, config, gprefs, \ open_url, open_local_file, \ ALL_COLUMNS, NONE, info_dialog, choose_files, \ warning_dialog, ResizableDialog, question_dialog @@ -343,9 +343,6 @@ class ConfigDialog(ResizableDialog, Ui_Dialog): self.model = library_view.model() self.db = self.model.db self.server = server - path = prefs['library_path'] - self.location.setText(path if path else '') - self.connect(self.browse_button, SIGNAL('clicked(bool)'), self.browse) self.connect(self.compact_button, SIGNAL('clicked(bool)'), self.compact) input_map = prefs['input_format_order'] @@ -808,12 +805,6 @@ class ConfigDialog(ResizableDialog, Ui_Dialog): d = CheckIntegrity(self.db, self) d.exec_() - def browse(self): - dir = choose_dir(self, 'database location dialog', - _('Select location for books')) - if dir: - self.location.setText(dir) - def accept(self): mcs = unicode(self.max_cover_size.text()).strip() if not re.match(r'\d+x\d+', mcs): @@ -834,7 +825,6 @@ class ConfigDialog(ResizableDialog, Ui_Dialog): config['use_roman_numerals_for_series_number'] = bool(self.roman_numerals.isChecked()) config['new_version_notification'] = bool(self.new_version_notification.isChecked()) prefs['network_timeout'] = int(self.timeout.value()) - path = unicode(self.location.text()) input_cols = [unicode(self.input_order.item(i).data(Qt.UserRole).toString()) for i in range(self.input_order.count())] prefs['input_format_order'] = input_cols @@ -875,24 +865,13 @@ class ConfigDialog(ResizableDialog, Ui_Dialog): val = self.opt_gui_layout.itemData(self.opt_gui_layout.currentIndex()).toString() config['gui_layout'] = unicode(val) - if not path or not os.path.exists(path) or not os.path.isdir(path): - d = error_dialog(self, _('Invalid database location'), - _('Invalid database location ')+path+ - _('
Must be a directory.')) - d.exec_() - elif not os.access(path, os.W_OK): - d = error_dialog(self, _('Invalid database location'), - _('Invalid database location.
Cannot write to ')+path) - d.exec_() - else: - self.database_location = os.path.abspath(path) - if must_restart: - warning_dialog(self, _('Must restart'), - _('The changes you made require that Calibre be ' - 'restarted. Please restart as soon as practical.'), - show=True, show_copy_button=False) - self.parent.must_restart_before_config = True - QDialog.accept(self) + if must_restart: + warning_dialog(self, _('Must restart'), + _('The changes you made require that Calibre be ' + 'restarted. Please restart as soon as practical.'), + show=True, show_copy_button=False) + self.parent.must_restart_before_config = True + QDialog.accept(self) class VacThread(QThread): diff --git a/src/calibre/gui2/dialogs/config/config.ui b/src/calibre/gui2/dialogs/config/config.ui index 5f890631b2..5d6bff2467 100644 --- a/src/calibre/gui2/dialogs/config/config.ui +++ b/src/calibre/gui2/dialogs/config/config.ui @@ -113,50 +113,6 @@ - - - - - - - 16777215 - 70 - - - - &Location of ebooks (The ebooks are stored in folders sorted by author and metadata is stored in the file metadata.db) - - - true - - - location - - - - - - - - - - - - Browse for the new database location - - - ... - - - - :/images/mimetypes/dir.svg:/images/mimetypes/dir.svg - - - - - - - diff --git a/src/calibre/gui2/dialogs/metadata_single.ui b/src/calibre/gui2/dialogs/metadata_single.ui index 4efb48d870..5da9d37d04 100644 --- a/src/calibre/gui2/dialogs/metadata_single.ui +++ b/src/calibre/gui2/dialogs/metadata_single.ui @@ -277,12 +277,6 @@ - - - 0 - 0 - - List of known series. You can add new series. @@ -295,9 +289,6 @@ QComboBox::InsertAlphabetically - - QComboBox::AdjustToContents - diff --git a/src/calibre/gui2/init.py b/src/calibre/gui2/init.py index 2474685522..254d2c3d00 100644 --- a/src/calibre/gui2/init.py +++ b/src/calibre/gui2/init.py @@ -7,14 +7,13 @@ __docformat__ = 'restructuredtext en' import functools, sys, os -from PyQt4.Qt import QMenu, Qt, pyqtSignal, QIcon, QStackedWidget, \ - QSize, QSizePolicy, QStatusBar, QUrl, QLabel, QFont +from PyQt4.Qt import QMenu, Qt, QStackedWidget, \ + QSize, QSizePolicy, QStatusBar, QLabel, QFont from calibre.utils.config import prefs -from calibre.ebooks import BOOK_EXTENSIONS from calibre.constants import isosx, __appname__, preferred_encoding, \ __version__ -from calibre.gui2 import config, is_widescreen, open_url +from calibre.gui2 import config, is_widescreen from calibre.gui2.library.views import BooksView, DeviceBooksView from calibre.gui2.widgets import Splitter from calibre.gui2.tag_view import TagBrowserWidget @@ -28,157 +27,6 @@ def partial(*args, **kwargs): _keep_refs.append(ans) return ans -class SaveMenu(QMenu): # {{{ - - save_fmt = pyqtSignal(object) - - def __init__(self, parent): - QMenu.__init__(self, _('Save single format to disk...'), parent) - for ext in sorted(BOOK_EXTENSIONS): - action = self.addAction(ext.upper()) - setattr(self, 'do_'+ext, partial(self.do, ext)) - action.triggered.connect( - getattr(self, 'do_'+ext)) - - def do(self, ext, *args): - self.save_fmt.emit(ext) - -# }}} - -class ToolbarMixin(object): # {{{ - - def __init__(self): - self.action_help.triggered.connect(self.show_help) - md = QMenu() - md.addAction(_('Edit metadata individually'), - partial(self.edit_metadata, False, bulk=False)) - md.addSeparator() - md.addAction(_('Edit metadata in bulk'), - partial(self.edit_metadata, False, bulk=True)) - md.addSeparator() - md.addAction(_('Download metadata and covers'), - partial(self.download_metadata, False, covers=True), - Qt.ControlModifier+Qt.Key_D) - md.addAction(_('Download only metadata'), - partial(self.download_metadata, False, covers=False)) - md.addAction(_('Download only covers'), - partial(self.download_metadata, False, covers=True, - set_metadata=False, set_social_metadata=False)) - md.addAction(_('Download only social metadata'), - partial(self.download_metadata, False, covers=False, - set_metadata=False, set_social_metadata=True)) - self.metadata_menu = md - - mb = QMenu() - mb.addAction(_('Merge into first selected book - delete others'), - self.merge_books) - mb.addSeparator() - mb.addAction(_('Merge into first selected book - keep others'), - partial(self.merge_books, safe_merge=True)) - self.merge_menu = mb - self.action_merge.setMenu(mb) - md.addSeparator() - md.addAction(self.action_merge) - - self.add_menu = QMenu() - self.add_menu.addAction(_('Add books from a single directory'), - self.add_books) - self.add_menu.addAction(_('Add books from directories, including ' - 'sub-directories (One book per directory, assumes every ebook ' - 'file is the same book in a different format)'), - self.add_recursive_single) - self.add_menu.addAction(_('Add books from directories, including ' - 'sub directories (Multiple books per directory, assumes every ' - 'ebook file is a different book)'), self.add_recursive_multiple) - self.add_menu.addAction(_('Add Empty book. (Book entry with no ' - 'formats)'), self.add_empty) - self.action_add.setMenu(self.add_menu) - self.action_add.triggered.connect(self.add_books) - self.action_del.triggered.connect(self.delete_books) - self.action_edit.triggered.connect(self.edit_metadata) - self.action_merge.triggered.connect(self.merge_books) - - self.action_save.triggered.connect(self.save_to_disk) - self.save_menu = QMenu() - self.save_menu.addAction(_('Save to disk'), partial(self.save_to_disk, - False)) - self.save_menu.addAction(_('Save to disk in a single directory'), - partial(self.save_to_single_dir, False)) - self.save_menu.addAction(_('Save only %s format to disk')% - prefs['output_format'].upper(), - partial(self.save_single_format_to_disk, False)) - self.save_menu.addAction( - _('Save only %s format to disk in a single directory')% - prefs['output_format'].upper(), - partial(self.save_single_fmt_to_single_dir, False)) - self.save_sub_menu = SaveMenu(self) - self.save_menu.addMenu(self.save_sub_menu) - self.save_sub_menu.save_fmt.connect(self.save_specific_format_disk) - - self.action_view.triggered.connect(self.view_book) - self.view_menu = QMenu() - self.view_menu.addAction(_('View'), partial(self.view_book, False)) - ac = self.view_menu.addAction(_('View specific format')) - ac.setShortcut((Qt.ControlModifier if isosx else Qt.AltModifier)+Qt.Key_V) - self.action_view.setMenu(self.view_menu) - ac.triggered.connect(self.view_specific_format, type=Qt.QueuedConnection) - - self.delete_menu = QMenu() - self.delete_menu.addAction(_('Remove selected books'), self.delete_books) - self.delete_menu.addAction( - _('Remove files of a specific format from selected books..'), - self.delete_selected_formats) - self.delete_menu.addAction( - _('Remove all formats from selected books, except...'), - self.delete_all_but_selected_formats) - self.delete_menu.addAction( - _('Remove covers from selected books'), self.delete_covers) - self.delete_menu.addSeparator() - self.delete_menu.addAction( - _('Remove matching books from device'), - self.remove_matching_books_from_device) - self.action_del.setMenu(self.delete_menu) - - self.action_open_containing_folder.setShortcut(Qt.Key_O) - self.addAction(self.action_open_containing_folder) - self.action_open_containing_folder.triggered.connect(self.view_folder) - self.action_sync.setShortcut(Qt.Key_D) - self.action_sync.setEnabled(True) - self.create_device_menu() - self.action_sync.triggered.connect( - self._sync_action_triggered) - - self.action_edit.setMenu(md) - self.action_save.setMenu(self.save_menu) - - cm = QMenu() - cm.addAction(_('Convert individually'), partial(self.convert_ebook, - False, bulk=False)) - cm.addAction(_('Bulk convert'), - partial(self.convert_ebook, False, bulk=True)) - cm.addSeparator() - ac = cm.addAction( - _('Create catalog of books in your calibre library')) - ac.triggered.connect(self.generate_catalog) - self.action_convert.setMenu(cm) - self.action_convert.triggered.connect(self.convert_ebook) - self.convert_menu = cm - - pm = QMenu() - pm.addAction(QIcon(I('config.svg')), _('Preferences'), self.do_config) - pm.addAction(QIcon(I('wizard.svg')), _('Run welcome wizard'), - self.run_wizard) - self.action_preferences.setMenu(pm) - self.preferences_menu = pm - for x in (self.preferences_action, self.action_preferences): - x.triggered.connect(self.do_config) - - def show_help(self, *args): - open_url(QUrl('http://calibre-ebook.com/user_manual')) - - -# }}} - class LibraryViewMixin(object): # {{{ def __init__(self, db): diff --git a/src/calibre/gui2/layout.py b/src/calibre/gui2/layout.py index c1ff3ab505..3116037685 100644 --- a/src/calibre/gui2/layout.py +++ b/src/calibre/gui2/layout.py @@ -6,108 +6,105 @@ __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' from operator import attrgetter +from functools import partial -from PyQt4.Qt import QIcon, Qt, QWidget, QAction, QToolBar, QSize, QVariant, \ - QAbstractListModel, QFont, QApplication, QPalette, pyqtSignal, QToolButton, \ - QModelIndex, QListView, QAbstractButton, QPainter, QPixmap, QColor, \ - QVBoxLayout, QSizePolicy, QLabel, QHBoxLayout +from PyQt4.Qt import QIcon, Qt, QWidget, QAction, QToolBar, QSize, \ + pyqtSignal, QToolButton, \ + QObject, QVBoxLayout, QSizePolicy, QLabel, QHBoxLayout, QActionGroup, \ + QMenu, QUrl -from calibre.constants import __appname__, filesystem_encoding +from calibre.constants import __appname__, isosx from calibre.gui2.search_box import SearchBox2, SavedSearchBox from calibre.gui2.throbber import ThrobbingButton -from calibre.gui2 import NONE, config +from calibre.gui2 import config, open_url from calibre.gui2.widgets import ComboBoxWithHelp from calibre import human_readable +from calibre.utils.config import prefs +from calibre.ebooks import BOOK_EXTENSIONS ICON_SIZE = 48 -# Location View {{{ +class SaveMenu(QMenu): # {{{ -class LocationModel(QAbstractListModel): # {{{ - - devicesChanged = pyqtSignal() + save_fmt = pyqtSignal(object) def __init__(self, parent): - QAbstractListModel.__init__(self, parent) - self.icons = [QVariant(QIcon(I('library.png'))), - QVariant(QIcon(I('reader.svg'))), - QVariant(QIcon(I('sd.svg'))), - QVariant(QIcon(I('sd.svg')))] - self.text = [_('Library\n%d books'), - _('Reader\n%s'), - _('Card A\n%s'), - _('Card B\n%s')] + QMenu.__init__(self, _('Save single format to disk...'), parent) + for ext in sorted(BOOK_EXTENSIONS): + action = self.addAction(ext.upper()) + setattr(self, 'do_'+ext, partial(self.do, ext)) + action.triggered.connect( + getattr(self, 'do_'+ext)) + + def do(self, ext, *args): + self.save_fmt.emit(ext) + +# }}} + +class LocationManager(QObject): # {{{ + + locations_changed = pyqtSignal() + unmount_device = pyqtSignal() + location_selected = pyqtSignal(object) + + def __init__(self, parent=None): + QObject.__init__(self, parent) self.free = [-1, -1, -1] self.count = 0 - self.highlight_row = 0 - self.library_tooltip = _('Click to see the books available on your computer') - self.tooltips = [ - self.library_tooltip, - _('Click to see the books in the main memory of your reader'), - _('Click to see the books on storage card A in your reader'), - _('Click to see the books on storage card B in your reader') - ] + self.location_actions = QActionGroup(self) + self.location_actions.setExclusive(True) + self.current_location = 'library' + self._mem = [] + self.tooltips = {} - def database_changed(self, db): - lp = db.library_path - if not isinstance(lp, unicode): - lp = lp.decode(filesystem_encoding, 'replace') - self.tooltips[0] = self.library_tooltip + '\n\n' + \ - _('Books located at') + ' ' + lp - self.dataChanged.emit(self.index(0), self.index(0)) + def ac(name, text, icon, tooltip): + icon = QIcon(I(icon)) + ac = self.location_actions.addAction(icon, text) + setattr(self, 'location_'+name, ac) + ac.setAutoRepeat(False) + ac.setCheckable(True) + receiver = partial(self._location_selected, name) + ac.triggered.connect(receiver) + self.tooltips[name] = tooltip + if name != 'library': + m = QMenu(parent) + self._mem.append(m) + a = m.addAction(icon, tooltip) + a.triggered.connect(receiver) + self._mem.append(a) + a = m.addAction(QIcon(I('eject.svg')), _('Eject this device')) + a.triggered.connect(self._eject_requested) + ac.setMenu(m) + self._mem.append(a) + else: + ac.setToolTip(tooltip) - def rowCount(self, *args): - return 1 + len([i for i in self.free if i >= 0]) + return ac - def get_device_row(self, row): - if row == 2 and self.free[1] == -1 and self.free[2] > -1: - row = 3 - return row + ac('library', _('Library'), 'lt.png', + _('Show books in calibre library')) + ac('main', _('Main'), 'reader.svg', + _('Show books in the main memory of the device')) + ac('carda', _('Card A'), 'sd.svg', + _('Show books in storage card A')) + ac('cardb', _('Card B'), 'sd.svg', + _('Show books in storage card B')) - def get_tooltip(self, row, drow): - ans = self.tooltips[row] - if row > 0: - fs = self.free[drow-1] - if fs > -1: - ans += '\n\n%s '%(human_readable(fs)) + _('free') - return ans + def _location_selected(self, location, *args): + if location != self.current_location and hasattr(self, + 'location_'+location): + self.current_location = location + self.location_selected.emit(location) + getattr(self, 'location_'+location).setChecked(True) - def data(self, index, role): - row = index.row() - drow = self.get_device_row(row) - data = NONE - if role == Qt.DisplayRole: - text = self.text[drow]%(human_readable(self.free[drow-1])) if row > 0 \ - else self.text[drow]%self.count - data = QVariant(text) - elif role == Qt.DecorationRole: - data = self.icons[drow] - elif role in (Qt.ToolTipRole, Qt.StatusTipRole): - ans = self.get_tooltip(row, drow) - data = QVariant(ans) - elif role == Qt.SizeHintRole: - data = QVariant(QSize(155, 90)) - elif role == Qt.FontRole: - font = QFont('monospace') - font.setBold(row == self.highlight_row) - data = QVariant(font) - elif role == Qt.ForegroundRole and row == self.highlight_row: - return QVariant(QApplication.palette().brush( - QPalette.HighlightedText)) - elif role == Qt.BackgroundRole and row == self.highlight_row: - return QVariant(QApplication.palette().brush( - QPalette.Highlight)) + def _eject_requested(self, *args): + self.unmount_device.emit() - return data - - def device_connected(self, dev): - self.icons[1] = QIcon(dev.icon) - self.dataChanged.emit(self.index(1), self.index(1)) - - def headerData(self, section, orientation, role): - return NONE - - def update_devices(self, cp=(None, None), fs=[-1, -1, -1]): + def update_devices(self, cp=(None, None), fs=[-1, -1, -1], icon=None): + if icon is None: + icon = I('reader.svg') + self.location_main.setIcon(QIcon(icon)) + had_device = self.has_device if cp is None: cp = (None, None) if isinstance(cp, (str, unicode)): @@ -120,137 +117,34 @@ class LocationModel(QAbstractListModel): # {{{ cpa, cpb = cp self.free[1] = fs[1] if fs[1] is not None and cpa is not None else -1 self.free[2] = fs[2] if fs[2] is not None and cpb is not None else -1 - self.reset() - self.devicesChanged.emit() + self.update_tooltips() + if self.has_device != had_device: + self.locations_changed.emit() + if not self.has_device: + self.location_library.trigger() - def location_changed(self, row): - self.highlight_row = row - self.dataChanged.emit( - self.index(0), self.index(self.rowCount(QModelIndex())-1)) + def update_tooltips(self): + for i, loc in enumerate(('main', 'carda', 'cardb')): + t = self.tooltips[loc] + if self.free[i] > -1: + t += u'\n\n%s '%human_readable(self.free[i]) + _('available') + ac = getattr(self, 'location_'+loc) + ac.setToolTip(t) + ac.setWhatsThis(t) + ac.setStatusTip(t) - def location_for_row(self, row): - if row == 0: return 'library' - if row == 1: return 'main' - if row == 3: return 'cardb' - return 'carda' if self.free[1] > -1 else 'cardb' - -# }}} - -class LocationView(QListView): - - umount_device = pyqtSignal() - location_selected = pyqtSignal(object) - - def __init__(self, parent): - QListView.__init__(self, parent) - self.setModel(LocationModel(self)) - self.reset() - self.currentChanged = self.current_changed - - self.eject_button = EjectButton(self) - self.eject_button.hide() - - self.entered.connect(self.item_entered) - self.viewportEntered.connect(self.viewport_entered) - self.eject_button.clicked.connect(self.eject_clicked) - self.model().devicesChanged.connect(self.eject_button.hide) - self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, - QSizePolicy.Expanding)) - self.setMouseTracking(True) - self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) - self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) - self.setEditTriggers(self.NoEditTriggers) - self.setTabKeyNavigation(True) - self.setProperty("showDropIndicator", True) - self.setSelectionMode(self.SingleSelection) - self.setIconSize(QSize(ICON_SIZE, ICON_SIZE)) - self.setMovement(self.Static) - self.setFlow(self.LeftToRight) - self.setGridSize(QSize(175, ICON_SIZE)) - self.setViewMode(self.ListMode) - self.setWordWrap(True) - self.setObjectName("location_view") - self.setMaximumSize(QSize(600, ICON_SIZE+16)) - self.setMinimumWidth(400) - - def eject_clicked(self, *args): - self.umount_device.emit() - - def count_changed(self, new_count): - self.model().count = new_count - self.model().reset() @property - def book_count(self): - return self.model().count - - def current_changed(self, current, previous): - if current.isValid(): - i = current.row() - location = self.model().location_for_row(i) - self.location_selected.emit(location) - self.model().location_changed(i) - - def location_changed(self, row): - if 0 <= row and row <= 3: - self.model().location_changed(row) - - def leaveEvent(self, event): - self.unsetCursor() - self.eject_button.hide() - - def item_entered(self, location): - self.setCursor(Qt.PointingHandCursor) - self.eject_button.hide() - - if location.row() == 1: - rect = self.visualRect(location) - - self.eject_button.resize(rect.height()/2, rect.height()/2) - - x, y = rect.left(), rect.top() - x = x + (rect.width() - self.eject_button.width() - 2) - y += 6 - - self.eject_button.move(x, y) - self.eject_button.show() - - def viewport_entered(self): - self.unsetCursor() - self.eject_button.hide() - - -class EjectButton(QAbstractButton): - - def __init__(self, parent): - QAbstractButton.__init__(self, parent) - self.mouse_over = False - self.setMouseTracking(True) - - def enterEvent(self, event): - self.mouse_over = True - QAbstractButton.enterEvent(self, event) - - def leaveEvent(self, event): - self.mouse_over = False - QAbstractButton.leaveEvent(self, event) - - def paintEvent(self, event): - painter = QPainter(self) - painter.setClipRect(event.rect()) - image = QPixmap(I('eject')).scaledToHeight(event.rect().height(), - Qt.SmoothTransformation) - - if not self.mouse_over: - alpha_mask = QPixmap(image.width(), image.height()) - color = QColor(128, 128, 128) - alpha_mask.fill(color) - image.setAlphaChannel(alpha_mask) - - painter.drawPixmap(0, 0, image) - - + def has_device(self): + return max(self.free) > -1 + @property + def available_actions(self): + ans = [self.location_library] + for i, loc in enumerate(('main', 'carda', 'cardb')): + if self.free[i] > -1: + ans.append(getattr(self, 'location_'+loc)) + return ans # }}} @@ -326,7 +220,7 @@ class SearchBar(QWidget): # {{{ class ToolBar(QToolBar): # {{{ - def __init__(self, actions, donate, location_view, parent=None): + def __init__(self, actions, donate, location_manager, parent=None): QToolBar.__init__(self, parent) self.setContextMenuPolicy(Qt.PreventContextMenu) self.setMovable(False) @@ -335,11 +229,12 @@ class ToolBar(QToolBar): # {{{ self.setAllowedAreas(Qt.TopToolBarArea|Qt.BottomToolBarArea) self.setIconSize(QSize(ICON_SIZE, ICON_SIZE)) self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) + self.setStyleSheet('QToolButton:checked { font-weight: bold }') - self.showing_device = False self.all_actions = actions self.donate = donate - self.location_view = location_view + self.location_manager = location_manager + self.location_manager.locations_changed.connect(self.build_bar) self.d_widget = QWidget() self.d_widget.setLayout(QVBoxLayout()) self.d_widget.layout().addWidget(donate) @@ -350,40 +245,45 @@ class ToolBar(QToolBar): # {{{ def contextMenuEvent(self, *args): pass - def device_status_changed(self, connected): - self.showing_device = connected - self.build_bar() - def build_bar(self): - order_field = 'device' if self.showing_device else 'normal' + showing_device = self.location_manager.has_device + order_field = 'device' if showing_device else 'normal' o = attrgetter(order_field+'_order') - sepvals = [2] if self.showing_device else [1] + sepvals = [2] if showing_device else [1] sepvals += [3] actions = [x for x in self.all_actions if o(x) > -1] actions.sort(cmp=lambda x,y : cmp(o(x), o(y))) self.clear() - for x in actions: - self.addAction(x) - ch = self.widgetForAction(x) + + + def setup_tool_button(ac): + ch = self.widgetForAction(ac) ch.setCursor(Qt.PointingHandCursor) ch.setAutoRaise(True) - - if x.action_name == 'choose_library': - self.location_action = self.addWidget(self.location_view) - self.choose_action = x - if config['show_donate_button']: - self.addWidget(self.d_widget) - if x.action_name not in ('choose_library', 'help'): + if ac.menu() is not None: ch.setPopupMode(ch.MenuButtonPopup) + for x in actions: + self.addAction(x) + setup_tool_button(x) + + if x.action_name == 'choose_library': + self.choose_action = x + if showing_device: + self.addSeparator() + for ac in self.location_manager.available_actions: + self.addAction(ac) + setup_tool_button(ac) + self.addSeparator() + self.location_manager.location_library.trigger() + elif config['show_donate_button']: + self.addWidget(self.d_widget) for x in actions: if x.separator_before in sepvals: self.insertSeparator(x) - - self.location_action.setVisible(self.showing_device) - self.choose_action.setVisible(not self.showing_device) + self.choose_action.setVisible(not showing_device) def count_changed(self, new_count): text = _('%d books')%new_count @@ -397,6 +297,9 @@ class ToolBar(QToolBar): # {{{ self.setToolButtonStyle(style) QToolBar.resizeEvent(self, ev) + def database_changed(self, db): + pass + # }}} class Action(QAction): @@ -405,6 +308,7 @@ class Action(QAction): class MainWindowMixin(object): def __init__(self): + self.device_connected = None self.setObjectName('MainWindow') self.setWindowIcon(QIcon(I('library.png'))) self.setWindowTitle(__appname__) @@ -417,9 +321,30 @@ class MainWindowMixin(object): self.resize(1012, 740) self.donate_button = ThrobbingButton(self.centralwidget) self.donate_button.set_normal_icon_size(ICON_SIZE, ICON_SIZE) + self.location_manager = LocationManager(self) - # Actions {{{ + all_actions = self.setup_actions() + self.search_bar = SearchBar(self) + self.tool_bar = ToolBar(all_actions, self.donate_button, + self.location_manager, self) + self.addToolBar(Qt.TopToolBarArea, self.tool_bar) + self.tool_bar.choose_action.triggered.connect(self.choose_library) + + l = self.centralwidget.layout() + l.addWidget(self.search_bar) + + + def read_toolbar_settings(self): + pass + + def choose_library(self, *args): + from calibre.gui2.dialogs.choose_library import ChooseLibrary + db = self.library_view.model().db + c = ChooseLibrary(db, self.library_moved, self) + c.exec_() + + def setup_actions(self): # {{{ all_actions = [] def ac(normal_order, device_order, separator_before, @@ -467,17 +392,135 @@ class MainWindowMixin(object): ac(-1, -1, 0, 'books_with_the_same_tags', _('Books with the same tags'), 'tags.svg') - # }}} + self.action_help.triggered.connect(self.show_help) + md = QMenu() + md.addAction(_('Edit metadata individually'), + partial(self.edit_metadata, False, bulk=False)) + md.addSeparator() + md.addAction(_('Edit metadata in bulk'), + partial(self.edit_metadata, False, bulk=True)) + md.addSeparator() + md.addAction(_('Download metadata and covers'), + partial(self.download_metadata, False, covers=True), + Qt.ControlModifier+Qt.Key_D) + md.addAction(_('Download only metadata'), + partial(self.download_metadata, False, covers=False)) + md.addAction(_('Download only covers'), + partial(self.download_metadata, False, covers=True, + set_metadata=False, set_social_metadata=False)) + md.addAction(_('Download only social metadata'), + partial(self.download_metadata, False, covers=False, + set_metadata=False, set_social_metadata=True)) + self.metadata_menu = md - self.location_view = LocationView(self.centralwidget) - self.search_bar = SearchBar(self) - self.tool_bar = ToolBar(all_actions, self.donate_button, self.location_view, self) - self.addToolBar(Qt.TopToolBarArea, self.tool_bar) + mb = QMenu() + mb.addAction(_('Merge into first selected book - delete others'), + self.merge_books) + mb.addSeparator() + mb.addAction(_('Merge into first selected book - keep others'), + partial(self.merge_books, safe_merge=True)) + self.merge_menu = mb + self.action_merge.setMenu(mb) + md.addSeparator() + md.addAction(self.action_merge) - l = self.centralwidget.layout() - l.addWidget(self.search_bar) + self.add_menu = QMenu() + self.add_menu.addAction(_('Add books from a single directory'), + self.add_books) + self.add_menu.addAction(_('Add books from directories, including ' + 'sub-directories (One book per directory, assumes every ebook ' + 'file is the same book in a different format)'), + self.add_recursive_single) + self.add_menu.addAction(_('Add books from directories, including ' + 'sub directories (Multiple books per directory, assumes every ' + 'ebook file is a different book)'), self.add_recursive_multiple) + self.add_menu.addAction(_('Add Empty book. (Book entry with no ' + 'formats)'), self.add_empty) + self.action_add.setMenu(self.add_menu) + self.action_add.triggered.connect(self.add_books) + self.action_del.triggered.connect(self.delete_books) + self.action_edit.triggered.connect(self.edit_metadata) + self.action_merge.triggered.connect(self.merge_books) + + self.action_save.triggered.connect(self.save_to_disk) + self.save_menu = QMenu() + self.save_menu.addAction(_('Save to disk'), partial(self.save_to_disk, + False)) + self.save_menu.addAction(_('Save to disk in a single directory'), + partial(self.save_to_single_dir, False)) + self.save_menu.addAction(_('Save only %s format to disk')% + prefs['output_format'].upper(), + partial(self.save_single_format_to_disk, False)) + self.save_menu.addAction( + _('Save only %s format to disk in a single directory')% + prefs['output_format'].upper(), + partial(self.save_single_fmt_to_single_dir, False)) + self.save_sub_menu = SaveMenu(self) + self.save_menu.addMenu(self.save_sub_menu) + self.save_sub_menu.save_fmt.connect(self.save_specific_format_disk) + + self.action_view.triggered.connect(self.view_book) + self.view_menu = QMenu() + self.view_menu.addAction(_('View'), partial(self.view_book, False)) + ac = self.view_menu.addAction(_('View specific format')) + ac.setShortcut((Qt.ControlModifier if isosx else Qt.AltModifier)+Qt.Key_V) + self.action_view.setMenu(self.view_menu) + ac.triggered.connect(self.view_specific_format, type=Qt.QueuedConnection) + + self.delete_menu = QMenu() + self.delete_menu.addAction(_('Remove selected books'), self.delete_books) + self.delete_menu.addAction( + _('Remove files of a specific format from selected books..'), + self.delete_selected_formats) + self.delete_menu.addAction( + _('Remove all formats from selected books, except...'), + self.delete_all_but_selected_formats) + self.delete_menu.addAction( + _('Remove covers from selected books'), self.delete_covers) + self.delete_menu.addSeparator() + self.delete_menu.addAction( + _('Remove matching books from device'), + self.remove_matching_books_from_device) + self.action_del.setMenu(self.delete_menu) + + self.action_open_containing_folder.setShortcut(Qt.Key_O) + self.addAction(self.action_open_containing_folder) + self.action_open_containing_folder.triggered.connect(self.view_folder) + self.action_sync.setShortcut(Qt.Key_D) + self.action_sync.setEnabled(True) + self.create_device_menu() + self.action_sync.triggered.connect( + self._sync_action_triggered) + + self.action_edit.setMenu(md) + self.action_save.setMenu(self.save_menu) + + cm = QMenu() + cm.addAction(_('Convert individually'), partial(self.convert_ebook, + False, bulk=False)) + cm.addAction(_('Bulk convert'), + partial(self.convert_ebook, False, bulk=True)) + cm.addSeparator() + ac = cm.addAction( + _('Create catalog of books in your calibre library')) + ac.triggered.connect(self.generate_catalog) + self.action_convert.setMenu(cm) + self.action_convert.triggered.connect(self.convert_ebook) + self.convert_menu = cm + + pm = QMenu() + pm.addAction(QIcon(I('config.svg')), _('Preferences'), self.do_config) + pm.addAction(QIcon(I('wizard.svg')), _('Run welcome wizard'), + self.run_wizard) + self.action_preferences.setMenu(pm) + self.preferences_menu = pm + for x in (self.preferences_action, self.action_preferences): + x.triggered.connect(self.do_config) + + return all_actions + # }}} + + def show_help(self, *args): + open_url(QUrl('http://calibre-ebook.com/user_manual')) - def read_toolbar_settings(self): - pass - diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index ba4c637932..eec03876e9 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -12,13 +12,13 @@ __docformat__ = 'restructuredtext en' import collections, os, sys, textwrap, time from Queue import Queue, Empty from threading import Thread -from PyQt4.Qt import Qt, SIGNAL, QObject, QTimer, \ +from PyQt4.Qt import Qt, SIGNAL, QTimer, \ QPixmap, QMenu, QIcon, pyqtSignal, \ QDialog, \ QSystemTrayIcon, QApplication, QKeySequence, QAction, \ QMessageBox, QHelpEvent -from calibre import prints, patheq +from calibre import prints from calibre.constants import __appname__, isosx from calibre.ptempfile import PersistentTemporaryFile from calibre.utils.config import prefs, dynamic @@ -27,7 +27,6 @@ from calibre.gui2 import error_dialog, GetMetadata, open_local_file, \ gprefs, max_available_height, config, info_dialog from calibre.gui2.cover_flow import CoverFlowMixin from calibre.gui2.widgets import ProgressIndicator -from calibre.gui2.wizard import move_library from calibre.gui2.dialogs.scheduler import Scheduler from calibre.gui2.update import UpdateMixin from calibre.gui2.main_window import MainWindow @@ -38,7 +37,7 @@ from calibre.gui2.dialogs.config import ConfigDialog from calibre.gui2.dialogs.book_info import BookInfo from calibre.library.database2 import LibraryDatabase2 -from calibre.gui2.init import ToolbarMixin, LibraryViewMixin, LayoutMixin +from calibre.gui2.init import LibraryViewMixin, LayoutMixin from calibre.gui2.search_box import SearchBoxMixin, SavedSearchBoxMixin from calibre.gui2.search_restriction_mixin import SearchRestrictionMixin from calibre.gui2.tag_view import TagBrowserMixin @@ -91,7 +90,7 @@ class SystemTrayIcon(QSystemTrayIcon): # {{{ # }}} -class Main(MainWindow, MainWindowMixin, DeviceMixin, ToolbarMixin, # {{{ +class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{ TagBrowserMixin, CoverFlowMixin, LibraryViewMixin, SearchBoxMixin, SavedSearchBoxMixin, SearchRestrictionMixin, LayoutMixin, UpdateMixin, AnnotationsAction, AddAction, DeleteAction, @@ -192,21 +191,14 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, ToolbarMixin, # {{{ ####################### Start spare job server ######################## QTimer.singleShot(1000, self.add_spare_server) - ####################### Location View ######################## - QObject.connect(self.location_view, - SIGNAL('location_selected(PyQt_PyObject)'), - self.location_selected) - QObject.connect(self.location_view, - SIGNAL('umount_device()'), - self.device_manager.umount_device) + ####################### Location Manager ######################## + self.location_manager.location_selected.connect(self.location_selected) + self.location_manager.unmount_device.connect(self.device_manager.umount_device) self.eject_action.triggered.connect(self.device_manager.umount_device) #################### Update notification ################### UpdateMixin.__init__(self, opts) - ####################### Setup Toolbar ##################### - ToolbarMixin.__init__(self) - ####################### Search boxes ######################## SavedSearchBoxMixin.__init__(self) SearchBoxMixin.__init__(self) @@ -218,7 +210,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, ToolbarMixin, # {{{ if self.system_tray_icon.isVisible() and opts.start_in_tray: self.hide_windows() - for t in (self.location_view, self.tool_bar): + for t in (self.tool_bar, ): self.library_view.model().count_changed_signal.connect \ (t.count_changed) if not gprefs.get('quick_start_guide_added', False): @@ -235,8 +227,8 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, ToolbarMixin, # {{{ self.db_images.reset() self.library_view.model().count_changed() - self.location_view.model().database_changed(self.library_view.model().db) - self.library_view.model().database_changed.connect(self.location_view.model().database_changed, + self.tool_bar.database_changed(self.library_view.model().db) + self.library_view.model().database_changed.connect(self.tool_bar.database_changed, type=Qt.QueuedConnection) ########################### Tags Browser ############################## @@ -396,10 +388,6 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, ToolbarMixin, # {{{ self.tags_view.recount() self.create_device_menu() self.set_device_menu_items_state(bool(self.device_connected)) - if not patheq(self.library_path, d.database_location): - newloc = d.database_location - move_library(self.library_path, newloc, self, - self.library_moved) def library_moved(self, newloc): if newloc is None: return diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index 97758482fc..994fa4575f 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -490,6 +490,7 @@ class EnComboBox(QComboBox): QComboBox.__init__(self, *args) self.setLineEdit(EnLineEdit(self)) self.setAutoCompletionCaseSensitivity(Qt.CaseSensitive) + self.setMinimumContentsLength(20) def text(self): return unicode(self.currentText()) diff --git a/src/calibre/library/catalog.py b/src/calibre/library/catalog.py index 21aa863031..a540a8a660 100644 --- a/src/calibre/library/catalog.py +++ b/src/calibre/library/catalog.py @@ -1,7 +1,9 @@ +# -*- coding: utf-8 -*- + __license__ = 'GPL v3' __copyright__ = '2010, Greg Riker ' -import datetime, htmlentitydefs, os, re, shutil +import datetime, htmlentitydefs, os, re, shutil, codecs from collections import namedtuple from copy import deepcopy @@ -9,6 +11,7 @@ from copy import deepcopy from xml.sax.saxutils import escape from calibre import filesystem_encoding, prints, prepare_string_for_xml, strftime +from calibre.constants import preferred_encoding from calibre.customize import CatalogPlugin from calibre.customize.conversion import OptionRecommendation, DummyReporter from calibre.ebooks.BeautifulSoup import BeautifulSoup, BeautifulStoneSoup, Tag, NavigableString @@ -21,6 +24,10 @@ FIELDS = ['all', 'author_sort', 'authors', 'comments', 'series_index', 'series', 'size', 'tags', 'timestamp', 'title', 'uuid'] +#Allowed fields for template +TEMPLATE_ALLOWED_FIELDS = [ 'author_sort', 'authors', 'id', 'isbn', 'pubdate', + 'publisher', 'series_index', 'series', 'tags', 'timestamp', 'title', 'uuid' ] + class CSV_XML(CatalogPlugin): 'CSV/XML catalog generator' @@ -89,17 +96,20 @@ class CSV_XML(CatalogPlugin): fields = self.get_output_fields(opts) if self.fmt == 'csv': - outfile = open(path_to_output, 'w') + outfile = codecs.open(path_to_output, 'w', 'utf8') # Output the field headers outfile.write(u'%s\n' % u','.join(fields)) # Output the entry fields for entry in data: - outstr = '' - for (x, field) in enumerate(fields): + outstr = [] + for field in fields: item = entry[field] - if field == 'formats': + if item is None: + outstr.append('""') + continue + elif field == 'formats': fmt_list = [] for format in item: fmt_list.append(format.rpartition('.')[2].lower()) @@ -111,18 +121,13 @@ class CSV_XML(CatalogPlugin): item = u'%s' % re.sub(r'[\D]', '', item) elif field in ['pubdate', 'timestamp']: item = isoformat(item) + elif field == 'comments': + item = item.replace(u'\r\n',u' ') + item = item.replace(u'\n',u' ') - if x < len(fields) - 1: - if item is not None: - outstr += u'"%s",' % unicode(item).replace('"','""') - else: - outstr += '"",' - else: - if item is not None: - outstr += u'"%s"\n' % unicode(item).replace('"','""') - else: - outstr += '""\n' - outfile.write(outstr.encode('utf-8')) + outstr.append(u'"%s"' % unicode(item).replace('"','""')) + + outfile.write(u','.join(outstr) + u'\n') outfile.close() elif self.fmt == 'xml': @@ -181,6 +186,329 @@ class CSV_XML(CatalogPlugin): f.write(etree.tostring(root, encoding='utf-8', xml_declaration=True, pretty_print=True)) +class BIBTEX(CatalogPlugin): + 'BIBTEX catalog generator' + + Option = namedtuple('Option', 'option, default, dest, action, help') + + name = 'Catalog_BIBTEX' + description = 'BIBTEX catalog generator' + supported_platforms = ['windows', 'osx', 'linux'] + author = 'Sengian' + version = (1, 0, 0) + file_types = set(['bib']) + + cli_options = [ + Option('--fields', + default = 'all', + dest = 'fields', + action = None, + help = _('The fields to output when cataloging books in the ' + 'database. Should be a comma-separated list of fields.\n' + 'Available fields: %s.\n' + "Default: '%%default'\n" + "Applies to: BIBTEX output format")%', '.join(FIELDS)), + + Option('--sort-by', + default = 'id', + dest = 'sort_by', + action = None, + help = _('Output field to sort on.\n' + 'Available fields: author_sort, id, rating, size, timestamp, title.\n' + "Default: '%default'\n" + "Applies to: BIBTEX output format")), + + Option('--create-citation', + default = 'True', + dest = 'impcit', + action = None, + help = _('Create a citation for BibTeX entries.\n' + 'Boolean value: True, False\n' + "Default: '%default'\n" + "Applies to: BIBTEX output format")), + + Option('--citation-template', + default = '{authors}{id}', + dest = 'bib_cit', + action = None, + help = _('The template for citation creation from database fields.\n' + ' Should be a template with {} enclosed fields.\n' + 'Available fields: %s.\n' + "Default: '%%default'\n" + "Applies to: BIBTEX output format")%', '.join(TEMPLATE_ALLOWED_FIELDS)), + + Option('--choose-encoding', + default = 'utf8', + dest = 'bibfile_enc', + action = None, + help = _('BibTeX file encoding output.\n' + 'Available types: utf8, cp1252, ascii.\n' + "Default: '%default'\n" + "Applies to: BIBTEX output format")), + + Option('--choose-encoding-configuration', + default = 'strict', + dest = 'bibfile_enctag', + action = None, + help = _('BibTeX file encoding flag.\n' + 'Available types: strict, replace, ignore, backslashreplace.\n' + "Default: '%default'\n" + "Applies to: BIBTEX output format")), + + Option('--entry-type', + default = 'book', + dest = 'bib_entry', + action = None, + help = _('Entry type for BibTeX catalog.\n' + 'Available types: book, misc, mixed.\n' + "Default: '%default'\n" + "Applies to: BIBTEX output format"))] + + def run(self, path_to_output, opts, db, notification=DummyReporter()): + + from types import StringType, UnicodeType + + from calibre.library.save_to_disk import preprocess_template + #Bibtex functions + from calibre.utils.bibtex import bibtex_author_format, utf8ToBibtex, ValidateCitationKey + + def create_bibtex_entry(entry, fields, mode, template_citation, + asccii_bibtex = True, citation_bibtex = True): + + #Bibtex doesn't like UTF-8 but keep unicode until writing + #Define starting chain or if book valid strict and not book return a Fail string + + bibtex_entry = [] + if mode != "misc" and check_entry_book_valid(entry) : + bibtex_entry.append(u'@book{') + elif mode != "book" : + bibtex_entry.append(u'@misc{') + else : + #case strict book + return '' + + if citation_bibtex : + # Citation tag + bibtex_entry.append(make_bibtex_citation(entry, template_citation, asccii_bibtex)) + bibtex_entry = [u' '.join(bibtex_entry)] + + for field in fields: + item = entry[field] + #check if the field should be included (none or empty) + if item is None: + continue + try: + if len(item) == 0 : + continue + except TypeError: + pass + + if field == 'authors' : + bibtex_entry.append(u'author = "%s"' % bibtex_author_format(item)) + + elif field in ['title', 'publisher', 'cover', 'uuid', + 'author_sort', 'series'] : + bibtex_entry.append(u'%s = "%s"' % (field, utf8ToBibtex(item, asccii_bibtex))) + + elif field == 'id' : + bibtex_entry.append(u'calibreid = "%s"' % int(item)) + + elif field == 'rating' : + bibtex_entry.append(u'rating = "%s"' % int(item)) + + elif field == 'size' : + bibtex_entry.append(u'%s = "%s octets"' % (field, int(item))) + + elif field == 'tags' : + #A list to flatten + bibtex_entry.append(u'tags = "%s"' % utf8ToBibtex(u', '.join(item), asccii_bibtex)) + + elif field == 'comments' : + #\n removal + item = item.replace(u'\r\n',u' ') + item = item.replace(u'\n',u' ') + bibtex_entry.append(u'note = "%s"' % utf8ToBibtex(item, asccii_bibtex)) + + elif field == 'isbn' : + # Could be 9, 10 or 13 digits + bibtex_entry.append(u'isbn = "%s"' % re.sub(u'[\D]', u'', item)) + + elif field == 'formats' : + item = u', '.join([format.rpartition('.')[2].lower() for format in item]) + bibtex_entry.append(u'formats = "%s"' % item) + + elif field == 'series_index' : + bibtex_entry.append(u'volume = "%s"' % int(item)) + + elif field == 'timestamp' : + bibtex_entry.append(u'timestamp = "%s"' % isoformat(item).partition('T')[0]) + + elif field == 'pubdate' : + bibtex_entry.append(u'year = "%s"' % item.year) + bibtex_entry.append(u'month = "%s"' % utf8ToBibtex(strftime("%b", item), + asccii_bibtex)) + + bibtex_entry = u',\n '.join(bibtex_entry) + bibtex_entry += u' }\n\n' + + return bibtex_entry + + def check_entry_book_valid(entry): + #Check that the required fields are ok for a book entry + for field in ['title', 'authors', 'publisher'] : + if entry[field] is None or len(entry[field]) == 0 : + return False + if entry['pubdate'] is None : + return False + else : + return True + + def make_bibtex_citation(entry, template_citation, asccii_bibtex): + + #define a function to replace the template entry by its value + def tpl_replace(objtplname) : + + tpl_field = re.sub(u'[\{\}]', u'', objtplname.group()) + + if tpl_field in TEMPLATE_ALLOWED_FIELDS : + if tpl_field in ['pubdate', 'timestamp'] : + tpl_field = isoformat(entry[tpl_field]).partition('T')[0] + elif tpl_field in ['tags', 'authors'] : + tpl_field =entry[tpl_field][0] + elif tpl_field in ['id', 'series_index'] : + tpl_field = str(entry[tpl_field]) + else : + tpl_field = entry[tpl_field] + return tpl_field + else: + return u'' + + if len(template_citation) >0 : + tpl_citation = utf8ToBibtex(ValidateCitationKey(re.sub(u'\{[^{}]*\}', + tpl_replace, template_citation)), asccii_bibtex) + + if len(tpl_citation) >0 : + return tpl_citation + + if len(entry["isbn"]) > 0 : + template_citation = u'%s' % re.sub(u'[\D]',u'', entry["isbn"]) + + else : + template_citation = u'%s' % str(entry["id"]) + + if asccii_bibtex : + return ValidateCitationKey(template_citation.encode('ascii', 'replace')) + else : + return ValidateCitationKey(template_citation) + + self.fmt = path_to_output.rpartition('.')[2] + self.notification = notification + + # Combobox options + bibfile_enc = ['utf8', 'cp1252', 'ascii'] + bibfile_enctag = ['strict', 'replace', 'ignore', 'backslashreplace'] + bib_entry = ['mixed', 'misc', 'book'] + + # Needed beacause CLI return str vs int by widget + try: + bibfile_enc = bibfile_enc[opts.bibfile_enc] + bibfile_enctag = bibfile_enctag[opts.bibfile_enctag] + bib_entry = bib_entry[opts.bib_entry] + except: + if opts.bibfile_enc in bibfile_enc : + bibfile_enc = opts.bibfile_enc + else : + log(" WARNING: incorrect --choose-encoding flag, revert to default") + bibfile_enc = bibfile_enc[0] + if opts.bibfile_enctag in bibfile_enctag : + bibfile_enctag = opts.bibfile_enctag + else : + log(" WARNING: incorrect --choose-encoding-configuration flag, revert to default") + bibfile_enctag = bibfile_enctag[0] + if opts.bib_entry in bib_entry : + bib_entry = opts.bib_entry + else : + log(" WARNING: incorrect --entry-type flag, revert to default") + bib_entry = bib_entry[0] + + if opts.verbose: + opts_dict = vars(opts) + log("%s(): Generating %s" % (self.name,self.fmt)) + if opts_dict['search_text']: + log(" --search='%s'" % opts_dict['search_text']) + + if opts_dict['ids']: + log(" Book count: %d" % len(opts_dict['ids'])) + if opts_dict['search_text']: + log(" (--search ignored when a subset of the database is specified)") + + if opts_dict['fields']: + if opts_dict['fields'] == 'all': + log(" Fields: %s" % ', '.join(FIELDS[1:])) + else: + log(" Fields: %s" % opts_dict['fields']) + + log(" Output file will be encoded in %s with %s flag" % (bibfile_enc, bibfile_enctag)) + + log(" BibTeX entry type is %s with a citation like '%s' flag" % (bib_entry, opts_dict['bib_cit'])) + + # If a list of ids are provided, don't use search_text + if opts.ids: + opts.search_text = None + + data = self.search_sort_db(db, opts) + + if not len(data): + log.error("\nNo matching database entries for search criteria '%s'" % opts.search_text) + + # Get the requested output fields as a list + fields = self.get_output_fields(opts) + + if not len(data): + log.error("\nNo matching database entries for search criteria '%s'" % opts.search_text) + + #Entries writing after Bibtex formating (or not) + if bibfile_enc != 'ascii' : + asccii_bibtex = False + else : + asccii_bibtex = True + + #Check and go to default in case of bad CLI + if isinstance(opts.impcit, (StringType, UnicodeType)) : + if opts.impcit == 'False' : + citation_bibtex= False + elif opts.impcit == 'True' : + citation_bibtex= True + else : + log(" WARNING: incorrect --create-citation, revert to default") + citation_bibtex= True + else : + citation_bibtex= opts.impcit + + template_citation = preprocess_template(opts.bib_cit) + + #Open output and write entries + outfile = codecs.open(path_to_output, 'w', bibfile_enc, bibfile_enctag) + + #File header + nb_entries = len(data) + + #check in book strict if all is ok else throw a warning into log + if bib_entry == 'book' : + nb_books = len(filter(check_entry_book_valid, data)) + if nb_books < nb_entries : + log(" WARNING: only %d entries in %d are book compatible" % (nb_books, nb_entries)) + nb_entries = nb_books + + outfile.write(u'%%%Calibre catalog\n%%%{0} entries in catalog\n\n'.format(nb_entries)) + outfile.write(u'@preamble{"This catalog of %d entries was generated by calibre on %s"}\n\n' + % (nb_entries, nowf().strftime("%A, %d. %B %Y %H:%M").decode(preferred_encoding))) + + for entry in data: + outfile.write(create_bibtex_entry(entry, fields, bib_entry, template_citation, + asccii_bibtex, citation_bibtex)) + + outfile.close() class EPUB_MOBI(CatalogPlugin): 'ePub catalog generator' diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 1534d3ffbf..85e951d4b0 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -116,6 +116,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): # missing functions self.books_list_filter = self.conn.create_dynamic_filter('books_list_filter') + @classmethod + def exists_at(cls, path): + return path and os.path.exists(os.path.join(path, 'metadata.db')) + def __init__(self, library_path, row_factory=False): self.field_metadata = FieldMetadata() if not os.path.exists(library_path): diff --git a/src/calibre/utils/bibtex.py b/src/calibre/utils/bibtex.py new file mode 100644 index 0000000000..7809781f7c --- /dev/null +++ b/src/calibre/utils/bibtex.py @@ -0,0 +1,2539 @@ +# -*- coding: utf-8 -*- + +""" Collection of python utility-methodes commonly used by other + bibliograph packages. + From http://pypi.python.org/pypi/bibliograph.core/ + from Tom Gross + + Adapted for calibre use + + Zope Public License (ZPL) Version 2.1 + + A copyright notice accompanies this license document that + identifies the copyright holders. + + This license has been certified as open source. It has also + been designated as GPL compatible by the Free Software + Foundation (FSF). + + Redistribution and use in source and binary forms, with or + without modification, are permitted provided that the + following conditions are met: + + 1. Redistributions in source code must retain the + accompanying copyright notice, this list of conditions, + and the following disclaimer. + + 2. Redistributions in binary form must reproduce the accompanying + copyright notice, this list of conditions, and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + + 3. Names of the copyright holders must not be used to + endorse or promote products derived from this software + without prior written permission from the copyright + holders. + + 4. The right to distribute this software or to use it for + any purpose does not give you the right to use + Servicemarks (sm) or Trademarks (tm) of the copyright + holders. Use of them is covered by separate agreement + with the copyright holders. + + 5. If any files are modified, you must cause the modified + files to carry prominent notices stating that you changed + the files and the date of any change. + + Disclaimer + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' + AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT + NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + NO EVENT SHALL THE COPYRIGHT HOLDERS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + DAMAGE. + """ + +__docformat__ = 'reStructuredText' +__author__ = 'sengian ' + +import re, string + +utf8enc2latex_mapping = { + # This is a mapping of Unicode characters to LaTeX equivalents. + # The information has been extracted from + # , written by + # David Carlisle and Sebastian Rahtz. + # + # The extraction has been done by the "create_unimap.py" script + # located at . + + #Fix some encoding problem between cp1252 and latin1 + # from http://www.microsoft.com/typography/unicode/1252.htm + u'\x80': '{\\mbox{\\texteuro}}', # EURO SIGN + u'\x82': '{,}', # SINGLE LOW-9 QUOTATION MARK + u'\x83': '$f$', # LATIN SMALL LETTER F WITH HOOK + u'\x84': '{,,}', # DOUBLE LOW-9 QUOTATION MARK + u'\x85': '{\\ldots}', # HORIZONTAL ELLIPSIS + u'\x86': '{\\textdagger}', # DAGGER + u'\x87': '{\\textdaggerdbl}', # DOUBLE DAGGER + u'\x88': '{\textasciicircum}', # MODIFIER LETTER CIRCUMFLEX ACCENT + u'\x89': '{\\textperthousand}', # PER MILLE SIGN + u'\x8A': '{\\v{S}}', # LATIN CAPITAL LETTER S WITH CARON + u'\x8B': '{\\guilsinglleft}', # SINGLE LEFT-POINTING ANGLE QUOTATION MARK + u'\x8C': '{\\OE}', # LATIN CAPITAL LIGATURE OE + u'\x8E': '{\\v{Z}}', # LATIN CAPITAL LETTER Z WITH CARON + u'\x91': '{`}', # LEFT SINGLE QUOTATION MARK + u'\x92': "{'}", # RIGHT SINGLE QUOTATION MARK + u'\x93': '{\\textquotedblleft}', # LEFT DOUBLE QUOTATION MARK + u'\x94': '{\\textquotedblright}', # RIGHT DOUBLE QUOTATION MARK + u'\x95': '{\\textbullet}', # BULLET + u'\x96': '{\\textendash}', # EN DASH + u'\x97': '{\\textemdash}', # EM DASH + u'\x98': '{\\texttildelow}', # SMALL TILDE + u'\x99': '{\\texttrademark}', # TRADE MARK SIGN + u'\x9A': '{\\v{s}}', # LATIN SMALL LETTER S WITH CARON + u'\x9B': '{\\guilsinglright}', # SINGLE RIGHT-POINTING ANGLE QUOTATION MARK + u'\x9C': '{\\oe}', # LATIN SMALL LIGATURE OE + u'\x9E': '{\\v{z}}', # LATIN SMALL LETTER Z WITH CARON + u'\x9F': '{\\"{Y}}', # LATIN CAPITAL LETTER Y WITH DIAERESIS + + u'\xa0': '$~$', + u'\xa1': '{\\textexclamdown}', + u'\xa2': '{\\textcent}', + u'\xa3': '{\\textsterling}', + u'\xa4': '{\\textcurrency}', + u'\xa5': '{\\textyen}', + u'\xa6': '{\\textbrokenbar}', + u'\xa7': '{\\textsection}', + u'\xa8': '{\\textasciidieresis}', + u'\xa9': '{\\textcopyright}', + u'\xaa': '{\\textordfeminine}', + u'\xab': '{\\guillemotleft}', + u'\xac': '$\\lnot$', + u'\xad': '$\\-$', + u'\xae': '{\\textregistered}', + u'\xaf': '{\\textasciimacron}', + u'\xb0': '{\\textdegree}', + u'\xb1': '$\\pm$', + u'\xb2': '${^2}$', + u'\xb3': '${^3}$', + u'\xb4': '{\\textasciiacute}', + u'\xb5': '$\\mathrm{\\mu}$', + u'\xb6': '{\\textparagraph}', + u'\xb7': '$\\cdot$', + u'\xb8': '{\\c{}}', + u'\xb9': '${^1}$', + u'\xba': '{\\textordmasculine}', + u'\xbb': '{\\guillemotright}', + u'\xbc': '{\\textonequarter}', + u'\xbd': '{\\textonehalf}', + u'\xbe': '{\\textthreequarters}', + u'\xbf': '{\\textquestiondown}', + u'\xc0': '{\\`{A}}', + u'\xc1': "{\\'{A}}", + u'\xc2': '{\\^{A}}', + u'\xc3': '{\\~{A}}', + u'\xc4': '{\\"{A}}', + u'\xc5': '{\\AA}', + u'\xc6': '{\\AE}', + u'\xc7': '{\\c{C}}', + u'\xc8': '{\\`{E}}', + u'\xc9': "{\\'{E}}", + u'\xca': '{\\^{E}}', + u'\xcb': '{\\"{E}}', + u'\xcc': '{\\`{I}}', + u'\xcd': "{\\'{I}}", + u'\xce': '{\\^{I}}', + u'\xcf': '{\\"{I}}', + u'\xd0': '{\\DH}', + u'\xd1': '{\\~{N}}', + u'\xd2': '{\\`{O}}', + u'\xd3': "{\\'{O}}", + u'\xd4': '{\\^{O}}', + u'\xd5': '{\\~{O}}', + u'\xd6': '{\\"{O}}', + u'\xd7': '{\\texttimes}', + u'\xd8': '{\\O}', + u'\xd9': '{\\`{U}}', + u'\xda': "{\\'{U}}", + u'\xdb': '{\\^{U}}', + u'\xdc': '{\\"{U}}', + u'\xdd': "{\\'{Y}}", + u'\xde': '{\\TH}', + u'\xdf': '{\\ss}', + u'\xe0': '{\\`{a}}', + u'\xe1': "{\\'{a}}", + u'\xe2': '{\\^{a}}', + u'\xe3': '{\\~{a}}', + u'\xe4': '{\\"{a}}', + u'\xe5': '{\\aa}', + u'\xe6': '{\\ae}', + u'\xe7': '{\\c{c}}', + u'\xe8': '{\\`{e}}', + u'\xe9': "{\\'{e}}", + u'\xea': '{\\^{e}}', + u'\xeb': '{\\"{e}}', + u'\xec': '{\\`{\\i}}', + u'\xed': "{\\'{\\i}}", + u'\xee': '{\\^{\\i}}', + u'\xef': '{\\"{\\i}}', + u'\xf0': '{\\dh}', + u'\xf1': '{\\~{n}}', + u'\xf2': '{\\`{o}}', + u'\xf3': "{\\'{o}}", + u'\xf4': '{\\^{o}}', + u'\xf5': '{\\~{o}}', + u'\xf6': '{\\"{o}}', + u'\xf7': '$\\div$', + u'\xf8': '{\\o}', + u'\xf9': '{\\`{u}}', + u'\xfa': "{\\'{u}}", + u'\xfb': '{\\^{u}}', + u'\xfc': '{\\"{u}}', + u'\xfd': "{\\'{y}}", + u'\xfe': '{\\th}', + u'\xff': '{\\"{y}}', + u'\u0100': '{\\={A}}', + u'\u0101': '{\\={a}}', + u'\u0102': '{\\u{A}}', + u'\u0103': '{\\u{a}}', + u'\u0104': '{\\k{A}}', + u'\u0105': '{\\k{a}}', + u'\u0106': "{\\'{C}}", + u'\u0107': "{\\'{c}}", + u'\u0108': '{\\^{C}}', + u'\u0109': '{\\^{c}}', + u'\u010a': '{\\.{C}}', + u'\u010b': '{\\.{c}}', + u'\u010c': '{\\v{C}}', + u'\u010d': '{\\v{c}}', + u'\u010e': '{\\v{D}}', + u'\u010f': '{\\v{d}}', + u'\u0110': '{\\DJ}', + u'\u0111': '{\\dj}', + u'\u0112': '{\\={E}}', + u'\u0113': '{\\={e}}', + u'\u0114': '{\\u{E}}', + u'\u0115': '{\\u{e}}', + u'\u0116': '{\\.{E}}', + u'\u0117': '{\\.{e}}', + u'\u0118': '{\\k{E}}', + u'\u0119': '{\\k{e}}', + u'\u011a': '{\\v{E}}', + u'\u011b': '{\\v{e}}', + u'\u011c': '{\\^{G}}', + u'\u011d': '{\\^{g}}', + u'\u011e': '{\\u{G}}', + u'\u011f': '{\\u{g}}', + u'\u0120': '{\\.{G}}', + u'\u0121': '{\\.{g}}', + u'\u0122': '{\\c{G}}', + u'\u0123': '{\\c{g}}', + u'\u0124': '{\\^{H}}', + u'\u0125': '{\\^{h}}', + u'\u0126': '{{\\fontencoding{LELA}\\selectfont\\char40}}', + u'\u0127': '$\\Elzxh$', + u'\u0128': '{\\~{I}}', + u'\u0129': '{\\~{\\i}}', + u'\u012a': '{\\={I}}', + u'\u012b': '{\\={\\i}}', + u'\u012c': '{\\u{I}}', + u'\u012d': '{\\u{\\i}}', + u'\u012e': '{\\k{I}}', + u'\u012f': '{\\k{i}}', + u'\u0130': '{\\.{I}}', + u'\u0131': '{\\i}', + u'\u0132': '{IJ}', + u'\u0133': '{ij}', + u'\u0134': '{\\^{J}}', + u'\u0135': '{\\^{\\j}}', + u'\u0136': '{\\c{K}}', + u'\u0137': '{\\c{k}}', + u'\u0138': '{{\\fontencoding{LELA}\\selectfont\\char91}}', + u'\u0139': "{\\'{L}}", + u'\u013a': "{\\'{l}}", + u'\u013b': '{\\c{L}}', + u'\u013c': '{\\c{l}}', + u'\u013d': '{\\v{L}}', + u'\u013e': '{\\v{l}}', + u'\u013f': '{{\\fontencoding{LELA}\\selectfont\\char201}}', + u'\u0140': '{{\\fontencoding{LELA}\\selectfont\\char202}}', + u'\u0141': '{\\L}', + u'\u0142': '{\\l}', + u'\u0143': "{\\'{N}}", + u'\u0144': "{\\'{n}}", + u'\u0145': '{\\c{N}}', + u'\u0146': '{\\c{n}}', + u'\u0147': '{\\v{N}}', + u'\u0148': '{\\v{n}}', + u'\u0149': "{'n}", + u'\u014a': '{\\NG}', + u'\u014b': '{\\ng}', + u'\u014c': '{\\={O}}', + u'\u014d': '{\\={o}}', + u'\u014e': '{\\u{O}}', + u'\u014f': '{\\u{o}}', + u'\u0150': '{\\H{O}}', + u'\u0151': '{\\H{o}}', + u'\u0152': '{\\OE}', + u'\u0153': '{\\oe}', + u'\u0154': "{\\'{R}}", + u'\u0155': "{\\'{r}}", + u'\u0156': '{\\c{R}}', + u'\u0157': '{\\c{r}}', + u'\u0158': '{\\v{R}}', + u'\u0159': '{\\v{r}}', + u'\u015a': "{\\'{S}}", + u'\u015b': "{\\'{s}}", + u'\u015c': '{\\^{S}}', + u'\u015d': '{\\^{s}}', + u'\u015e': '{\\c{S}}', + u'\u015f': '{\\c{s}}', + u'\u0160': '{\\v{S}}', + u'\u0161': '{\\v{s}}', + u'\u0162': '{\\c{T}}', + u'\u0163': '{\\c{t}}', + u'\u0164': '{\\v{T}}', + u'\u0165': '{\\v{t}}', + u'\u0166': '{{\\fontencoding{LELA}\\selectfont\\char47}}', + u'\u0167': '{{\\fontencoding{LELA}\\selectfont\\char63}}', + u'\u0168': '{\\~{U}}', + u'\u0169': '{\\~{u}}', + u'\u016a': '{\\={U}}', + u'\u016b': '{\\={u}}', + u'\u016c': '{\\u{U}}', + u'\u016d': '{\\u{u}}', + u'\u016e': '{\\r{U}}', + u'\u016f': '{\\r{u}}', + u'\u0170': '{\\H{U}}', + u'\u0171': '{\\H{u}}', + u'\u0172': '{\\k{U}}', + u'\u0173': '{\\k{u}}', + u'\u0174': '{\\^{W}}', + u'\u0175': '{\\^{w}}', + u'\u0176': '{\\^{Y}}', + u'\u0177': '{\\^{y}}', + u'\u0178': '{\\"{Y}}', + u'\u0179': "{\\'{Z}}", + u'\u017a': "{\\'{z}}", + u'\u017b': '{\\.{Z}}', + u'\u017c': '{\\.{z}}', + u'\u017d': '{\\v{Z}}', + u'\u017e': '{\\v{z}}', + u'\u0192': '$f$', + u'\u0195': '{\\texthvlig}', + u'\u019e': '{\\textnrleg}', + u'\u01aa': '$\\eth$', + u'\u01ba': '{{\\fontencoding{LELA}\\selectfont\\char195}}', + u'\u01c2': '{\\textdoublepipe}', + u'\u01f5': "{\\'{g}}", + u'\u0250': '$\\Elztrna$', + u'\u0252': '$\\Elztrnsa$', + u'\u0254': '$\\Elzopeno$', + u'\u0256': '$\\Elzrtld$', + u'\u0258': '{{\\fontencoding{LEIP}\\selectfont\\char61}}', + u'\u0259': '$\\Elzschwa$', + u'\u025b': '$\\varepsilon$', + u'\u0261': '{g}', + u'\u0263': '$\\Elzpgamma$', + u'\u0264': '$\\Elzpbgam$', + u'\u0265': '$\\Elztrnh$', + u'\u026c': '$\\Elzbtdl$', + u'\u026d': '$\\Elzrtll$', + u'\u026f': '$\\Elztrnm$', + u'\u0270': '$\\Elztrnmlr$', + u'\u0271': '$\\Elzltlmr$', + u'\u0272': '{\\Elzltln}', + u'\u0273': '$\\Elzrtln$', + u'\u0277': '$\\Elzclomeg$', + u'\u0278': '{\\textphi}', + u'\u0279': '$\\Elztrnr$', + u'\u027a': '$\\Elztrnrl$', + u'\u027b': '$\\Elzrttrnr$', + u'\u027c': '$\\Elzrl$', + u'\u027d': '$\\Elzrtlr$', + u'\u027e': '$\\Elzfhr$', + u'\u027f': '{{\\fontencoding{LEIP}\\selectfont\\char202}}', + u'\u0282': '$\\Elzrtls$', + u'\u0283': '$\\Elzesh$', + u'\u0287': '$\\Elztrnt$', + u'\u0288': '$\\Elzrtlt$', + u'\u028a': '$\\Elzpupsil$', + u'\u028b': '$\\Elzpscrv$', + u'\u028c': '$\\Elzinvv$', + u'\u028d': '$\\Elzinvw$', + u'\u028e': '$\\Elztrny$', + u'\u0290': '$\\Elzrtlz$', + u'\u0292': '$\\Elzyogh$', + u'\u0294': '$\\Elzglst$', + u'\u0295': '$\\Elzreglst$', + u'\u0296': '$\\Elzinglst$', + u'\u029e': '{\\textturnk}', + u'\u02a4': '$\\Elzdyogh$', + u'\u02a7': '$\\Elztesh$', + u'\u02bc': "{'}", + u'\u02c7': '{\\textasciicaron}', + u'\u02c8': '$\\Elzverts$', + u'\u02cc': '$\\Elzverti$', + u'\u02d0': '$\\Elzlmrk$', + u'\u02d1': '$\\Elzhlmrk$', + u'\u02d2': '$\\Elzsbrhr$', + u'\u02d3': '$\\Elzsblhr$', + u'\u02d4': '$\\Elzrais$', + u'\u02d5': '$\\Elzlow$', + u'\u02d8': '{\\textasciibreve}', + u'\u02d9': '{\\textperiodcentered}', + u'\u02da': '{\\r{}}', + u'\u02db': '{\\k{}}', + u'\u02dc': '{\\texttildelow}', + u'\u02dd': '{\\H{}}', + u'\u02e5': '{\\tone{55}}', + u'\u02e6': '{\\tone{44}}', + u'\u02e7': '{\\tone{33}}', + u'\u02e8': '{\\tone{22}}', + u'\u02e9': '{\\tone{11}}', + u'\u0300': '{\\`}', + u'\u0301': "{\\'}", + u'\u0302': '{\\^}', + u'\u0303': '{\\~}', + u'\u0304': '{\\=}', + u'\u0306': '{\\u}', + u'\u0307': '{\\.}', + u'\u0308': '{\\"}', + u'\u030a': '{\\r}', + u'\u030b': '{\\H}', + u'\u030c': '{\\v}', + u'\u030f': '{\\cyrchar\\C}', + u'\u0311': '{{\\fontencoding{LECO}\\selectfont\\char177}}', + u'\u0318': '{{\\fontencoding{LECO}\\selectfont\\char184}}', + u'\u0319': '{{\\fontencoding{LECO}\\selectfont\\char185}}', + u'\u0321': '$\\Elzpalh$', + u'\u0322': '{\\Elzrh}', + u'\u0327': '{\\c}', + u'\u0328': '{\\k}', + u'\u032a': '$\\Elzsbbrg$', + u'\u032b': '{{\\fontencoding{LECO}\\selectfont\\char203}}', + u'\u032f': '{{\\fontencoding{LECO}\\selectfont\\char207}}', + u'\u0335': '{\\Elzxl}', + u'\u0336': '{\\Elzbar}', + u'\u0337': '{{\\fontencoding{LECO}\\selectfont\\char215}}', + u'\u0338': '{{\\fontencoding{LECO}\\selectfont\\char216}}', + u'\u033a': '{{\\fontencoding{LECO}\\selectfont\\char218}}', + u'\u033b': '{{\\fontencoding{LECO}\\selectfont\\char219}}', + u'\u033c': '{{\\fontencoding{LECO}\\selectfont\\char220}}', + u'\u033d': '{{\\fontencoding{LECO}\\selectfont\\char221}}', + u'\u0361': '{{\\fontencoding{LECO}\\selectfont\\char225}}', + u'\u0386': "{\\'{A}}", + u'\u0388': "{\\'{E}}", + u'\u0389': "{\\'{H}}", + u'\u038a': "{\\'{}{I}}", + u'\u038c': "{\\'{}O}", + u'\u038e': "$\\mathrm{'Y}$", + u'\u038f': "$\\mathrm{'\\Omega}$", + u'\u0390': '$\\acute{\\ddot{\\iota}}$', + u'\u0391': '$\\Alpha$', + u'\u0392': '$\\Beta$', + u'\u0393': '$\\Gamma$', + u'\u0394': '$\\Delta$', + u'\u0395': '$\\Epsilon$', + u'\u0396': '$\\Zeta$', + u'\u0397': '$\\Eta$', + u'\u0398': '$\\Theta$', + u'\u0399': '$\\Iota$', + u'\u039a': '$\\Kappa$', + u'\u039b': '$\\Lambda$', + u'\u039c': '$M$', + u'\u039d': '$N$', + u'\u039e': '$\\Xi$', + u'\u039f': '$O$', + u'\u03a0': '$\\Pi$', + u'\u03a1': '$\\Rho$', + u'\u03a3': '$\\Sigma$', + u'\u03a4': '$\\Tau$', + u'\u03a5': '$\\Upsilon$', + u'\u03a6': '$\\Phi$', + u'\u03a7': '$\\Chi$', + u'\u03a8': '$\\Psi$', + u'\u03a9': '$\\Omega$', + u'\u03aa': '$\\mathrm{\\ddot{I}}$', + u'\u03ab': '$\\mathrm{\\ddot{Y}}$', + u'\u03ac': "{\\'{$\\alpha$}}", + u'\u03ad': '$\\acute{\\epsilon}$', + u'\u03ae': '$\\acute{\\eta}$', + u'\u03af': '$\\acute{\\iota}$', + u'\u03b0': '$\\acute{\\ddot{\\upsilon}}$', + u'\u03b1': '$\\alpha$', + u'\u03b2': '$\\beta$', + u'\u03b3': '$\\gamma$', + u'\u03b4': '$\\delta$', + u'\u03b5': '$\\epsilon$', + u'\u03b6': '$\\zeta$', + u'\u03b7': '$\\eta$', + u'\u03b8': '{\\texttheta}', + u'\u03b9': '$\\iota$', + u'\u03ba': '$\\kappa$', + u'\u03bb': '$\\lambda$', + u'\u03bc': '$\\mu$', + u'\u03bd': '$\\nu$', + u'\u03be': '$\\xi$', + u'\u03bf': '$o$', + u'\u03c0': '$\\pi$', + u'\u03c1': '$\\rho$', + u'\u03c2': '$\\varsigma$', + u'\u03c3': '$\\sigma$', + u'\u03c4': '$\\tau$', + u'\u03c5': '$\\upsilon$', + u'\u03c6': '$\\varphi$', + u'\u03c7': '$\\chi$', + u'\u03c8': '$\\psi$', + u'\u03c9': '$\\omega$', + u'\u03ca': '$\\ddot{\\iota}$', + u'\u03cb': '$\\ddot{\\upsilon}$', + u'\u03cc': "{\\'{o}}", + u'\u03cd': '$\\acute{\\upsilon}$', + u'\u03ce': '$\\acute{\\omega}$', + u'\u03d0': '{\\Pisymbol{ppi022}{87}}', + u'\u03d1': '{\\textvartheta}', + u'\u03d2': '$\\Upsilon$', + u'\u03d5': '$\\phi$', + u'\u03d6': '$\\varpi$', + u'\u03da': '$\\Stigma$', + u'\u03dc': '$\\Digamma$', + u'\u03dd': '$\\digamma$', + u'\u03de': '$\\Koppa$', + u'\u03e0': '$\\Sampi$', + u'\u03f0': '$\\varkappa$', + u'\u03f1': '$\\varrho$', + u'\u03f4': '{\\textTheta}', + u'\u03f6': '$\\backepsilon$', + u'\u0401': '{\\cyrchar\\CYRYO}', + u'\u0402': '{\\cyrchar\\CYRDJE}', + u'\u0403': "{\\cyrchar{\\'\\CYRG}}", + u'\u0404': '{\\cyrchar\\CYRIE}', + u'\u0405': '{\\cyrchar\\CYRDZE}', + u'\u0406': '{\\cyrchar\\CYRII}', + u'\u0407': '{\\cyrchar\\CYRYI}', + u'\u0408': '{\\cyrchar\\CYRJE}', + u'\u0409': '{\\cyrchar\\CYRLJE}', + u'\u040a': '{\\cyrchar\\CYRNJE}', + u'\u040b': '{\\cyrchar\\CYRTSHE}', + u'\u040c': "{\\cyrchar{\\'\\CYRK}}", + u'\u040e': '{\\cyrchar\\CYRUSHRT}', + u'\u040f': '{\\cyrchar\\CYRDZHE}', + u'\u0410': '{\\cyrchar\\CYRA}', + u'\u0411': '{\\cyrchar\\CYRB}', + u'\u0412': '{\\cyrchar\\CYRV}', + u'\u0413': '{\\cyrchar\\CYRG}', + u'\u0414': '{\\cyrchar\\CYRD}', + u'\u0415': '{\\cyrchar\\CYRE}', + u'\u0416': '{\\cyrchar\\CYRZH}', + u'\u0417': '{\\cyrchar\\CYRZ}', + u'\u0418': '{\\cyrchar\\CYRI}', + u'\u0419': '{\\cyrchar\\CYRISHRT}', + u'\u041a': '{\\cyrchar\\CYRK}', + u'\u041b': '{\\cyrchar\\CYRL}', + u'\u041c': '{\\cyrchar\\CYRM}', + u'\u041d': '{\\cyrchar\\CYRN}', + u'\u041e': '{\\cyrchar\\CYRO}', + u'\u041f': '{\\cyrchar\\CYRP}', + u'\u0420': '{\\cyrchar\\CYRR}', + u'\u0421': '{\\cyrchar\\CYRS}', + u'\u0422': '{\\cyrchar\\CYRT}', + u'\u0423': '{\\cyrchar\\CYRU}', + u'\u0424': '{\\cyrchar\\CYRF}', + u'\u0425': '{\\cyrchar\\CYRH}', + u'\u0426': '{\\cyrchar\\CYRC}', + u'\u0427': '{\\cyrchar\\CYRCH}', + u'\u0428': '{\\cyrchar\\CYRSH}', + u'\u0429': '{\\cyrchar\\CYRSHCH}', + u'\u042a': '{\\cyrchar\\CYRHRDSN}', + u'\u042b': '{\\cyrchar\\CYRERY}', + u'\u042c': '{\\cyrchar\\CYRSFTSN}', + u'\u042d': '{\\cyrchar\\CYREREV}', + u'\u042e': '{\\cyrchar\\CYRYU}', + u'\u042f': '{\\cyrchar\\CYRYA}', + u'\u0430': '{\\cyrchar\\cyra}', + u'\u0431': '{\\cyrchar\\cyrb}', + u'\u0432': '{\\cyrchar\\cyrv}', + u'\u0433': '{\\cyrchar\\cyrg}', + u'\u0434': '{\\cyrchar\\cyrd}', + u'\u0435': '{\\cyrchar\\cyre}', + u'\u0436': '{\\cyrchar\\cyrzh}', + u'\u0437': '{\\cyrchar\\cyrz}', + u'\u0438': '{\\cyrchar\\cyri}', + u'\u0439': '{\\cyrchar\\cyrishrt}', + u'\u043a': '{\\cyrchar\\cyrk}', + u'\u043b': '{\\cyrchar\\cyrl}', + u'\u043c': '{\\cyrchar\\cyrm}', + u'\u043d': '{\\cyrchar\\cyrn}', + u'\u043e': '{\\cyrchar\\cyro}', + u'\u043f': '{\\cyrchar\\cyrp}', + u'\u0440': '{\\cyrchar\\cyrr}', + u'\u0441': '{\\cyrchar\\cyrs}', + u'\u0442': '{\\cyrchar\\cyrt}', + u'\u0443': '{\\cyrchar\\cyru}', + u'\u0444': '{\\cyrchar\\cyrf}', + u'\u0445': '{\\cyrchar\\cyrh}', + u'\u0446': '{\\cyrchar\\cyrc}', + u'\u0447': '{\\cyrchar\\cyrch}', + u'\u0448': '{\\cyrchar\\cyrsh}', + u'\u0449': '{\\cyrchar\\cyrshch}', + u'\u044a': '{\\cyrchar\\cyrhrdsn}', + u'\u044b': '{\\cyrchar\\cyrery}', + u'\u044c': '{\\cyrchar\\cyrsftsn}', + u'\u044d': '{\\cyrchar\\cyrerev}', + u'\u044e': '{\\cyrchar\\cyryu}', + u'\u044f': '{\\cyrchar\\cyrya}', + u'\u0451': '{\\cyrchar\\cyryo}', + u'\u0452': '{\\cyrchar\\cyrdje}', + u'\u0453': "{\\cyrchar{\\'\\cyrg}}", + u'\u0454': '{\\cyrchar\\cyrie}', + u'\u0455': '{\\cyrchar\\cyrdze}', + u'\u0456': '{\\cyrchar\\cyrii}', + u'\u0457': '{\\cyrchar\\cyryi}', + u'\u0458': '{\\cyrchar\\cyrje}', + u'\u0459': '{\\cyrchar\\cyrlje}', + u'\u045a': '{\\cyrchar\\cyrnje}', + u'\u045b': '{\\cyrchar\\cyrtshe}', + u'\u045c': "{\\cyrchar{\\'\\cyrk}}", + u'\u045e': '{\\cyrchar\\cyrushrt}', + u'\u045f': '{\\cyrchar\\cyrdzhe}', + u'\u0460': '{\\cyrchar\\CYROMEGA}', + u'\u0461': '{\\cyrchar\\cyromega}', + u'\u0462': '{\\cyrchar\\CYRYAT}', + u'\u0464': '{\\cyrchar\\CYRIOTE}', + u'\u0465': '{\\cyrchar\\cyriote}', + u'\u0466': '{\\cyrchar\\CYRLYUS}', + u'\u0467': '{\\cyrchar\\cyrlyus}', + u'\u0468': '{\\cyrchar\\CYRIOTLYUS}', + u'\u0469': '{\\cyrchar\\cyriotlyus}', + u'\u046a': '{\\cyrchar\\CYRBYUS}', + u'\u046c': '{\\cyrchar\\CYRIOTBYUS}', + u'\u046d': '{\\cyrchar\\cyriotbyus}', + u'\u046e': '{\\cyrchar\\CYRKSI}', + u'\u046f': '{\\cyrchar\\cyrksi}', + u'\u0470': '{\\cyrchar\\CYRPSI}', + u'\u0471': '{\\cyrchar\\cyrpsi}', + u'\u0472': '{\\cyrchar\\CYRFITA}', + u'\u0474': '{\\cyrchar\\CYRIZH}', + u'\u0478': '{\\cyrchar\\CYRUK}', + u'\u0479': '{\\cyrchar\\cyruk}', + u'\u047a': '{\\cyrchar\\CYROMEGARND}', + u'\u047b': '{\\cyrchar\\cyromegarnd}', + u'\u047c': '{\\cyrchar\\CYROMEGATITLO}', + u'\u047d': '{\\cyrchar\\cyromegatitlo}', + u'\u047e': '{\\cyrchar\\CYROT}', + u'\u047f': '{\\cyrchar\\cyrot}', + u'\u0480': '{\\cyrchar\\CYRKOPPA}', + u'\u0481': '{\\cyrchar\\cyrkoppa}', + u'\u0482': '{\\cyrchar\\cyrthousands}', + u'\u0488': '{\\cyrchar\\cyrhundredthousands}', + u'\u0489': '{\\cyrchar\\cyrmillions}', + u'\u048c': '{\\cyrchar\\CYRSEMISFTSN}', + u'\u048d': '{\\cyrchar\\cyrsemisftsn}', + u'\u048e': '{\\cyrchar\\CYRRTICK}', + u'\u048f': '{\\cyrchar\\cyrrtick}', + u'\u0490': '{\\cyrchar\\CYRGUP}', + u'\u0491': '{\\cyrchar\\cyrgup}', + u'\u0492': '{\\cyrchar\\CYRGHCRS}', + u'\u0493': '{\\cyrchar\\cyrghcrs}', + u'\u0494': '{\\cyrchar\\CYRGHK}', + u'\u0495': '{\\cyrchar\\cyrghk}', + u'\u0496': '{\\cyrchar\\CYRZHDSC}', + u'\u0497': '{\\cyrchar\\cyrzhdsc}', + u'\u0498': '{\\cyrchar\\CYRZDSC}', + u'\u0499': '{\\cyrchar\\cyrzdsc}', + u'\u049a': '{\\cyrchar\\CYRKDSC}', + u'\u049b': '{\\cyrchar\\cyrkdsc}', + u'\u049c': '{\\cyrchar\\CYRKVCRS}', + u'\u049d': '{\\cyrchar\\cyrkvcrs}', + u'\u049e': '{\\cyrchar\\CYRKHCRS}', + u'\u049f': '{\\cyrchar\\cyrkhcrs}', + u'\u04a0': '{\\cyrchar\\CYRKBEAK}', + u'\u04a1': '{\\cyrchar\\cyrkbeak}', + u'\u04a2': '{\\cyrchar\\CYRNDSC}', + u'\u04a3': '{\\cyrchar\\cyrndsc}', + u'\u04a4': '{\\cyrchar\\CYRNG}', + u'\u04a5': '{\\cyrchar\\cyrng}', + u'\u04a6': '{\\cyrchar\\CYRPHK}', + u'\u04a7': '{\\cyrchar\\cyrphk}', + u'\u04a8': '{\\cyrchar\\CYRABHHA}', + u'\u04a9': '{\\cyrchar\\cyrabhha}', + u'\u04aa': '{\\cyrchar\\CYRSDSC}', + u'\u04ab': '{\\cyrchar\\cyrsdsc}', + u'\u04ac': '{\\cyrchar\\CYRTDSC}', + u'\u04ad': '{\\cyrchar\\cyrtdsc}', + u'\u04ae': '{\\cyrchar\\CYRY}', + u'\u04af': '{\\cyrchar\\cyry}', + u'\u04b0': '{\\cyrchar\\CYRYHCRS}', + u'\u04b1': '{\\cyrchar\\cyryhcrs}', + u'\u04b2': '{\\cyrchar\\CYRHDSC}', + u'\u04b3': '{\\cyrchar\\cyrhdsc}', + u'\u04b4': '{\\cyrchar\\CYRTETSE}', + u'\u04b5': '{\\cyrchar\\cyrtetse}', + u'\u04b6': '{\\cyrchar\\CYRCHRDSC}', + u'\u04b7': '{\\cyrchar\\cyrchrdsc}', + u'\u04b8': '{\\cyrchar\\CYRCHVCRS}', + u'\u04b9': '{\\cyrchar\\cyrchvcrs}', + u'\u04ba': '{\\cyrchar\\CYRSHHA}', + u'\u04bb': '{\\cyrchar\\cyrshha}', + u'\u04bc': '{\\cyrchar\\CYRABHCH}', + u'\u04bd': '{\\cyrchar\\cyrabhch}', + u'\u04be': '{\\cyrchar\\CYRABHCHDSC}', + u'\u04bf': '{\\cyrchar\\cyrabhchdsc}', + u'\u04c0': '{\\cyrchar\\CYRpalochka}', + u'\u04c3': '{\\cyrchar\\CYRKHK}', + u'\u04c4': '{\\cyrchar\\cyrkhk}', + u'\u04c7': '{\\cyrchar\\CYRNHK}', + u'\u04c8': '{\\cyrchar\\cyrnhk}', + u'\u04cb': '{\\cyrchar\\CYRCHLDSC}', + u'\u04cc': '{\\cyrchar\\cyrchldsc}', + u'\u04d4': '{\\cyrchar\\CYRAE}', + u'\u04d5': '{\\cyrchar\\cyrae}', + u'\u04d8': '{\\cyrchar\\CYRSCHWA}', + u'\u04d9': '{\\cyrchar\\cyrschwa}', + u'\u04e0': '{\\cyrchar\\CYRABHDZE}', + u'\u04e1': '{\\cyrchar\\cyrabhdze}', + u'\u04e8': '{\\cyrchar\\CYROTLD}', + u'\u04e9': '{\\cyrchar\\cyrotld}', + u'\u2002': '{\\hspace{0.6em}}', + u'\u2003': '{\\hspace{1em}}', + u'\u2004': '{\\hspace{0.33em}}', + u'\u2005': '{\\hspace{0.25em}}', + u'\u2006': '{\\hspace{0.166em}}', + u'\u2007': '{\\hphantom{0}}', + u'\u2008': '{\\hphantom{,}}', + u'\u2009': '{\\hspace{0.167em}}', + u'\u200a': '$\\mkern1mu$', + u'\u2010': '{-}', + u'\u2013': '{\\textendash}', + u'\u2014': '{\\textemdash}', + u'\u2015': '{\\rule{1em}{1pt}}', + u'\u2016': '$\\Vert$', + u'\u2018': '{`}', + u'\u2019': "{'}", + u'\u201a': '{,}', + u'\u201b': '$\\Elzreapos$', + u'\u201c': '{\\textquotedblleft}', + u'\u201d': '{\\textquotedblright}', + u'\u201e': '{,,}', + u'\u2020': '{\\textdagger}', + u'\u2021': '{\\textdaggerdbl}', + u'\u2022': '{\\textbullet}', + u'\u2024': '{.}', + u'\u2025': '{..}', + u'\u2026': '{\\ldots}', + u'\u2030': '{\\textperthousand}', + u'\u2031': '{\\textpertenthousand}', + u'\u2032': "${'}$", + u'\u2033': "${''}$", + u'\u2034': "${'''}$", + u'\u2035': '$\\backprime$', + u'\u2039': '{\\guilsinglleft}', + u'\u203a': '{\\guilsinglright}', + u'\u2057': "$''''$", + u'\u205f': '{\\mkern4mu}', + u'\u2060': '{\\nolinebreak}', + u'\u20a7': '{\\ensuremath{\\Elzpes}}', + u'\u20ac': '{\\mbox{\\texteuro}}', + u'\u20db': '$\\dddot$', + u'\u20dc': '$\\ddddot$', + u'\u2102': '$\\mathbb{C}$', + u'\u210a': '{\\mathscr{g}}', + u'\u210b': '$\\mathscr{H}$', + u'\u210c': '$\\mathfrak{H}$', + u'\u210d': '$\\mathbb{H}$', + u'\u210f': '$\\hslash$', + u'\u2110': '$\\mathscr{I}$', + u'\u2111': '$\\mathfrak{I}$', + u'\u2112': '$\\mathscr{L}$', + u'\u2113': '$\\mathscr{l}$', + u'\u2115': '$\\mathbb{N}$', + u'\u2116': '{\\cyrchar\\textnumero}', + u'\u2118': '$\\wp$', + u'\u2119': '$\\mathbb{P}$', + u'\u211a': '$\\mathbb{Q}$', + u'\u211b': '$\\mathscr{R}$', + u'\u211c': '$\\mathfrak{R}$', + u'\u211d': '$\\mathbb{R}$', + u'\u211e': '$\\Elzxrat$', + u'\u2122': '{\\texttrademark}', + u'\u2124': '$\\mathbb{Z}$', + u'\u2126': '$\\Omega$', + u'\u2127': '$\\mho$', + u'\u2128': '$\\mathfrak{Z}$', + u'\u2129': '$\\ElsevierGlyph{2129}$', + u'\u212b': '{\\AA}', + u'\u212c': '$\\mathscr{B}$', + u'\u212d': '$\\mathfrak{C}$', + u'\u212f': '$\\mathscr{e}$', + u'\u2130': '$\\mathscr{E}$', + u'\u2131': '$\\mathscr{F}$', + u'\u2133': '$\\mathscr{M}$', + u'\u2134': '$\\mathscr{o}$', + u'\u2135': '$\\aleph$', + u'\u2136': '$\\beth$', + u'\u2137': '$\\gimel$', + u'\u2138': '$\\daleth$', + u'\u2153': '$\\textfrac{1}{3}$', + u'\u2154': '$\\textfrac{2}{3}$', + u'\u2155': '$\\textfrac{1}{5}$', + u'\u2156': '$\\textfrac{2}{5}$', + u'\u2157': '$\\textfrac{3}{5}$', + u'\u2158': '$\\textfrac{4}{5}$', + u'\u2159': '$\\textfrac{1}{6}$', + u'\u215a': '$\\textfrac{5}{6}$', + u'\u215b': '$\\textfrac{1}{8}$', + u'\u215c': '$\\textfrac{3}{8}$', + u'\u215d': '$\\textfrac{5}{8}$', + u'\u215e': '$\\textfrac{7}{8}$', + u'\u2190': '$\\leftarrow$', + u'\u2191': '$\\uparrow$', + u'\u2192': '$\\rightarrow$', + u'\u2193': '$\\downarrow$', + u'\u2194': '$\\leftrightarrow$', + u'\u2195': '$\\updownarrow$', + u'\u2196': '$\\nwarrow$', + u'\u2197': '$\\nearrow$', + u'\u2198': '$\\searrow$', + u'\u2199': '$\\swarrow$', + u'\u219a': '$\\nleftarrow$', + u'\u219b': '$\\nrightarrow$', + u'\u219c': '$\\arrowwaveright$', + u'\u219d': '$\\arrowwaveright$', + u'\u219e': '$\\twoheadleftarrow$', + u'\u21a0': '$\\twoheadrightarrow$', + u'\u21a2': '$\\leftarrowtail$', + u'\u21a3': '$\\rightarrowtail$', + u'\u21a6': '$\\mapsto$', + u'\u21a9': '$\\hookleftarrow$', + u'\u21aa': '$\\hookrightarrow$', + u'\u21ab': '$\\looparrowleft$', + u'\u21ac': '$\\looparrowright$', + u'\u21ad': '$\\leftrightsquigarrow$', + u'\u21ae': '$\\nleftrightarrow$', + u'\u21b0': '$\\Lsh$', + u'\u21b1': '$\\Rsh$', + u'\u21b3': '$\\ElsevierGlyph{21B3}$', + u'\u21b6': '$\\curvearrowleft$', + u'\u21b7': '$\\curvearrowright$', + u'\u21ba': '$\\circlearrowleft$', + u'\u21bb': '$\\circlearrowright$', + u'\u21bc': '$\\leftharpoonup$', + u'\u21bd': '$\\leftharpoondown$', + u'\u21be': '$\\upharpoonright$', + u'\u21bf': '$\\upharpoonleft$', + u'\u21c0': '$\\rightharpoonup$', + u'\u21c1': '$\\rightharpoondown$', + u'\u21c2': '$\\downharpoonright$', + u'\u21c3': '$\\downharpoonleft$', + u'\u21c4': '$\\rightleftarrows$', + u'\u21c5': '$\\dblarrowupdown$', + u'\u21c6': '$\\leftrightarrows$', + u'\u21c7': '$\\leftleftarrows$', + u'\u21c8': '$\\upuparrows$', + u'\u21c9': '$\\rightrightarrows$', + u'\u21ca': '$\\downdownarrows$', + u'\u21cb': '$\\leftrightharpoons$', + u'\u21cc': '$\\rightleftharpoons$', + u'\u21cd': '$\\nLeftarrow$', + u'\u21ce': '$\\nLeftrightarrow$', + u'\u21cf': '$\\nRightarrow$', + u'\u21d0': '$\\Leftarrow$', + u'\u21d1': '$\\Uparrow$', + u'\u21d2': '$\\Rightarrow$', + u'\u21d3': '$\\Downarrow$', + u'\u21d4': '$\\Leftrightarrow$', + u'\u21d5': '$\\Updownarrow$', + u'\u21da': '$\\Lleftarrow$', + u'\u21db': '$\\Rrightarrow$', + u'\u21dd': '$\\rightsquigarrow$', + u'\u21f5': '$\\DownArrowUpArrow$', + u'\u2200': '$\\forall$', + u'\u2201': '$\\complement$', + u'\u2202': '$\\partial$', + u'\u2203': '$\\exists$', + u'\u2204': '$\\nexists$', + u'\u2205': '$\\varnothing$', + u'\u2207': '$\\nabla$', + u'\u2208': '$\\in$', + u'\u2209': '$\\not\\in$', + u'\u220b': '$\\ni$', + u'\u220c': '$\\not\\ni$', + u'\u220f': '$\\prod$', + u'\u2210': '$\\coprod$', + u'\u2211': '$\\sum$', + u'\u2212': '{-}', + u'\u2213': '$\\mp$', + u'\u2214': '$\\dotplus$', + u'\u2216': '$\\setminus$', + u'\u2217': '${_\\ast}$', + u'\u2218': '$\\circ$', + u'\u2219': '$\\bullet$', + u'\u221a': '$\\surd$', + u'\u221d': '$\\propto$', + u'\u221e': '$\\infty$', + u'\u221f': '$\\rightangle$', + u'\u2220': '$\\angle$', + u'\u2221': '$\\measuredangle$', + u'\u2222': '$\\sphericalangle$', + u'\u2223': '$\\mid$', + u'\u2224': '$\\nmid$', + u'\u2225': '$\\parallel$', + u'\u2226': '$\\nparallel$', + u'\u2227': '$\\wedge$', + u'\u2228': '$\\vee$', + u'\u2229': '$\\cap$', + u'\u222a': '$\\cup$', + u'\u222b': '$\\int$', + u'\u222c': '$\\int\\!\\int$', + u'\u222d': '$\\int\\!\\int\\!\\int$', + u'\u222e': '$\\oint$', + u'\u222f': '$\\surfintegral$', + u'\u2230': '$\\volintegral$', + u'\u2231': '$\\clwintegral$', + u'\u2232': '$\\ElsevierGlyph{2232}$', + u'\u2233': '$\\ElsevierGlyph{2233}$', + u'\u2234': '$\\therefore$', + u'\u2235': '$\\because$', + u'\u2237': '$\\Colon$', + u'\u2238': '$\\ElsevierGlyph{2238}$', + u'\u223a': '$\\mathbin{{:}\\!\\!{-}\\!\\!{:}}$', + u'\u223b': '$\\homothetic$', + u'\u223c': '$\\sim$', + u'\u223d': '$\\backsim$', + u'\u223e': '$\\lazysinv$', + u'\u2240': '$\\wr$', + u'\u2241': '$\\not\\sim$', + u'\u2242': '$\\ElsevierGlyph{2242}$', + u'\u2243': '$\\simeq$', + u'\u2244': '$\\not\\simeq$', + u'\u2245': '$\\cong$', + u'\u2246': '$\\approxnotequal$', + u'\u2247': '$\\not\\cong$', + u'\u2248': '$\\approx$', + u'\u2249': '$\\not\\approx$', + u'\u224a': '$\\approxeq$', + u'\u224b': '$\\tildetrpl$', + u'\u224c': '$\\allequal$', + u'\u224d': '$\\asymp$', + u'\u224e': '$\\Bumpeq$', + u'\u224f': '$\\bumpeq$', + u'\u2250': '$\\doteq$', + u'\u2251': '$\\doteqdot$', + u'\u2252': '$\\fallingdotseq$', + u'\u2253': '$\\risingdotseq$', + u'\u2254': '{:=}', + u'\u2255': '$=:$', + u'\u2256': '$\\eqcirc$', + u'\u2257': '$\\circeq$', + u'\u2259': '$\\estimates$', + u'\u225a': '$\\ElsevierGlyph{225A}$', + u'\u225b': '$\\starequal$', + u'\u225c': '$\\triangleq$', + u'\u225f': '$\\ElsevierGlyph{225F}$', + u'\u2260': '$\\not =$', + u'\u2261': '$\\equiv$', + u'\u2262': '$\\not\\equiv$', + u'\u2264': '$\\leq$', + u'\u2265': '$\\geq$', + u'\u2266': '$\\leqq$', + u'\u2267': '$\\geqq$', + u'\u2268': '$\\lneqq$', + u'\u2269': '$\\gneqq$', + u'\u226a': '$\\ll$', + u'\u226b': '$\\gg$', + u'\u226c': '$\\between$', + u'\u226d': '$\\not\\kern-0.3em\\times$', + u'\u226e': '$\\not<$', + u'\u226f': '$\\not>$', + u'\u2270': '$\\not\\leq$', + u'\u2271': '$\\not\\geq$', + u'\u2272': '$\\lessequivlnt$', + u'\u2273': '$\\greaterequivlnt$', + u'\u2274': '$\\ElsevierGlyph{2274}$', + u'\u2275': '$\\ElsevierGlyph{2275}$', + u'\u2276': '$\\lessgtr$', + u'\u2277': '$\\gtrless$', + u'\u2278': '$\\notlessgreater$', + u'\u2279': '$\\notgreaterless$', + u'\u227a': '$\\prec$', + u'\u227b': '$\\succ$', + u'\u227c': '$\\preccurlyeq$', + u'\u227d': '$\\succcurlyeq$', + u'\u227e': '$\\precapprox$', + u'\u227f': '$\\succapprox$', + u'\u2280': '$\\not\\prec$', + u'\u2281': '$\\not\\succ$', + u'\u2282': '$\\subset$', + u'\u2283': '$\\supset$', + u'\u2284': '$\\not\\subset$', + u'\u2285': '$\\not\\supset$', + u'\u2286': '$\\subseteq$', + u'\u2287': '$\\supseteq$', + u'\u2288': '$\\not\\subseteq$', + u'\u2289': '$\\not\\supseteq$', + u'\u228a': '$\\subsetneq$', + u'\u228b': '$\\supsetneq$', + u'\u228e': '$\\uplus$', + u'\u228f': '$\\sqsubset$', + u'\u2290': '$\\sqsupset$', + u'\u2291': '$\\sqsubseteq$', + u'\u2292': '$\\sqsupseteq$', + u'\u2293': '$\\sqcap$', + u'\u2294': '$\\sqcup$', + u'\u2295': '$\\oplus$', + u'\u2296': '$\\ominus$', + u'\u2297': '$\\otimes$', + u'\u2298': '$\\oslash$', + u'\u2299': '$\\odot$', + u'\u229a': '$\\circledcirc$', + u'\u229b': '$\\circledast$', + u'\u229d': '$\\circleddash$', + u'\u229e': '$\\boxplus$', + u'\u229f': '$\\boxminus$', + u'\u22a0': '$\\boxtimes$', + u'\u22a1': '$\\boxdot$', + u'\u22a2': '$\\vdash$', + u'\u22a3': '$\\dashv$', + u'\u22a4': '$\\top$', + u'\u22a5': '$\\perp$', + u'\u22a7': '$\\truestate$', + u'\u22a8': '$\\forcesextra$', + u'\u22a9': '$\\Vdash$', + u'\u22aa': '$\\Vvdash$', + u'\u22ab': '$\\VDash$', + u'\u22ac': '$\\nvdash$', + u'\u22ad': '$\\nvDash$', + u'\u22ae': '$\\nVdash$', + u'\u22af': '$\\nVDash$', + u'\u22b2': '$\\vartriangleleft$', + u'\u22b3': '$\\vartriangleright$', + u'\u22b4': '$\\trianglelefteq$', + u'\u22b5': '$\\trianglerighteq$', + u'\u22b6': '$\\original$', + u'\u22b7': '$\\image$', + u'\u22b8': '$\\multimap$', + u'\u22b9': '$\\hermitconjmatrix$', + u'\u22ba': '$\\intercal$', + u'\u22bb': '$\\veebar$', + u'\u22be': '$\\rightanglearc$', + u'\u22c0': '$\\ElsevierGlyph{22C0}$', + u'\u22c1': '$\\ElsevierGlyph{22C1}$', + u'\u22c2': '$\\bigcap$', + u'\u22c3': '$\\bigcup$', + u'\u22c4': '$\\diamond$', + u'\u22c5': '$\\cdot$', + u'\u22c6': '$\\star$', + u'\u22c7': '$\\divideontimes$', + u'\u22c8': '$\\bowtie$', + u'\u22c9': '$\\ltimes$', + u'\u22ca': '$\\rtimes$', + u'\u22cb': '$\\leftthreetimes$', + u'\u22cc': '$\\rightthreetimes$', + u'\u22cd': '$\\backsimeq$', + u'\u22ce': '$\\curlyvee$', + u'\u22cf': '$\\curlywedge$', + u'\u22d0': '$\\Subset$', + u'\u22d1': '$\\Supset$', + u'\u22d2': '$\\Cap$', + u'\u22d3': '$\\Cup$', + u'\u22d4': '$\\pitchfork$', + u'\u22d6': '$\\lessdot$', + u'\u22d7': '$\\gtrdot$', + u'\u22d8': '$\\verymuchless$', + u'\u22d9': '$\\verymuchgreater$', + u'\u22da': '$\\lesseqgtr$', + u'\u22db': '$\\gtreqless$', + u'\u22de': '$\\curlyeqprec$', + u'\u22df': '$\\curlyeqsucc$', + u'\u22e2': '$\\not\\sqsubseteq$', + u'\u22e3': '$\\not\\sqsupseteq$', + u'\u22e5': '$\\Elzsqspne$', + u'\u22e6': '$\\lnsim$', + u'\u22e7': '$\\gnsim$', + u'\u22e8': '$\\precedesnotsimilar$', + u'\u22e9': '$\\succnsim$', + u'\u22ea': '$\\ntriangleleft$', + u'\u22eb': '$\\ntriangleright$', + u'\u22ec': '$\\ntrianglelefteq$', + u'\u22ed': '$\\ntrianglerighteq$', + u'\u22ee': '$\\vdots$', + u'\u22ef': '$\\cdots$', + u'\u22f0': '$\\upslopeellipsis$', + u'\u22f1': '$\\downslopeellipsis$', + u'\u2305': '{\\barwedge}', + u'\u2306': '$\\perspcorrespond$', + u'\u2308': '$\\lceil$', + u'\u2309': '$\\rceil$', + u'\u230a': '$\\lfloor$', + u'\u230b': '$\\rfloor$', + u'\u2315': '$\\recorder$', + u'\u2316': '$\\mathchar"2208$', + u'\u231c': '$\\ulcorner$', + u'\u231d': '$\\urcorner$', + u'\u231e': '$\\llcorner$', + u'\u231f': '$\\lrcorner$', + u'\u2322': '$\\frown$', + u'\u2323': '$\\smile$', + u'\u2329': '$\\langle$', + u'\u232a': '$\\rangle$', + u'\u233d': '$\\ElsevierGlyph{E838}$', + u'\u23a3': '$\\Elzdlcorn$', + u'\u23b0': '$\\lmoustache$', + u'\u23b1': '$\\rmoustache$', + u'\u2423': '{\\textvisiblespace}', + u'\u2460': '{\\ding{172}}', + u'\u2461': '{\\ding{173}}', + u'\u2462': '{\\ding{174}}', + u'\u2463': '{\\ding{175}}', + u'\u2464': '{\\ding{176}}', + u'\u2465': '{\\ding{177}}', + u'\u2466': '{\\ding{178}}', + u'\u2467': '{\\ding{179}}', + u'\u2468': '{\\ding{180}}', + u'\u2469': '{\\ding{181}}', + u'\u24c8': '$\\circledS$', + u'\u2506': '$\\Elzdshfnc$', + u'\u2519': '$\\Elzsqfnw$', + u'\u2571': '$\\diagup$', + u'\u25a0': '{\\ding{110}}', + u'\u25a1': '$\\square$', + u'\u25aa': '$\\blacksquare$', + u'\u25ad': '$\\fbox{~~}$', + u'\u25af': '$\\Elzvrecto$', + u'\u25b1': '$\\ElsevierGlyph{E381}$', + u'\u25b2': '{\\ding{115}}', + u'\u25b3': '$\\bigtriangleup$', + u'\u25b4': '$\\blacktriangle$', + u'\u25b5': '$\\vartriangle$', + u'\u25b8': '$\\blacktriangleright$', + u'\u25b9': '$\\triangleright$', + u'\u25bc': '{\\ding{116}}', + u'\u25bd': '$\\bigtriangledown$', + u'\u25be': '$\\blacktriangledown$', + u'\u25bf': '$\\triangledown$', + u'\u25c2': '$\\blacktriangleleft$', + u'\u25c3': '$\\triangleleft$', + u'\u25c6': '{\\ding{117}}', + u'\u25ca': '$\\lozenge$', + u'\u25cb': '$\\bigcirc$', + u'\u25cf': '{\\ding{108}}', + u'\u25d0': '$\\Elzcirfl$', + u'\u25d1': '$\\Elzcirfr$', + u'\u25d2': '$\\Elzcirfb$', + u'\u25d7': '{\\ding{119}}', + u'\u25d8': '$\\Elzrvbull$', + u'\u25e7': '$\\Elzsqfl$', + u'\u25e8': '$\\Elzsqfr$', + u'\u25ea': '$\\Elzsqfse$', + u'\u25ef': '$\\bigcirc$', + u'\u2605': '{\\ding{72}}', + u'\u2606': '{\\ding{73}}', + u'\u260e': '{\\ding{37}}', + u'\u261b': '{\\ding{42}}', + u'\u261e': '{\\ding{43}}', + u'\u263e': '{\\rightmoon}', + u'\u263f': '{\\mercury}', + u'\u2640': '{\\venus}', + u'\u2642': '{\\male}', + u'\u2643': '{\\jupiter}', + u'\u2644': '{\\saturn}', + u'\u2645': '{\\uranus}', + u'\u2646': '{\\neptune}', + u'\u2647': '{\\pluto}', + u'\u2648': '{\\aries}', + u'\u2649': '{\\taurus}', + u'\u264a': '{\\gemini}', + u'\u264b': '{\\cancer}', + u'\u264c': '{\\leo}', + u'\u264d': '{\\virgo}', + u'\u264e': '{\\libra}', + u'\u264f': '{\\scorpio}', + u'\u2650': '{\\sagittarius}', + u'\u2651': '{\\capricornus}', + u'\u2652': '{\\aquarius}', + u'\u2653': '{\\pisces}', + u'\u2660': '{\\ding{171}}', + u'\u2662': '$\\diamond$', + u'\u2663': '{\\ding{168}}', + u'\u2665': '{\\ding{170}}', + u'\u2666': '{\\ding{169}}', + u'\u2669': '{\\quarternote}', + u'\u266a': '{\\eighthnote}', + u'\u266d': '$\\flat$', + u'\u266e': '$\\natural$', + u'\u266f': '$\\sharp$', + u'\u2701': '{\\ding{33}}', + u'\u2702': '{\\ding{34}}', + u'\u2703': '{\\ding{35}}', + u'\u2704': '{\\ding{36}}', + u'\u2706': '{\\ding{38}}', + u'\u2707': '{\\ding{39}}', + u'\u2708': '{\\ding{40}}', + u'\u2709': '{\\ding{41}}', + u'\u270c': '{\\ding{44}}', + u'\u270d': '{\\ding{45}}', + u'\u270e': '{\\ding{46}}', + u'\u270f': '{\\ding{47}}', + u'\u2710': '{\\ding{48}}', + u'\u2711': '{\\ding{49}}', + u'\u2712': '{\\ding{50}}', + u'\u2713': '{\\ding{51}}', + u'\u2714': '{\\ding{52}}', + u'\u2715': '{\\ding{53}}', + u'\u2716': '{\\ding{54}}', + u'\u2717': '{\\ding{55}}', + u'\u2718': '{\\ding{56}}', + u'\u2719': '{\\ding{57}}', + u'\u271a': '{\\ding{58}}', + u'\u271b': '{\\ding{59}}', + u'\u271c': '{\\ding{60}}', + u'\u271d': '{\\ding{61}}', + u'\u271e': '{\\ding{62}}', + u'\u271f': '{\\ding{63}}', + u'\u2720': '{\\ding{64}}', + u'\u2721': '{\\ding{65}}', + u'\u2722': '{\\ding{66}}', + u'\u2723': '{\\ding{67}}', + u'\u2724': '{\\ding{68}}', + u'\u2725': '{\\ding{69}}', + u'\u2726': '{\\ding{70}}', + u'\u2727': '{\\ding{71}}', + u'\u2729': '{\\ding{73}}', + u'\u272a': '{\\ding{74}}', + u'\u272b': '{\\ding{75}}', + u'\u272c': '{\\ding{76}}', + u'\u272d': '{\\ding{77}}', + u'\u272e': '{\\ding{78}}', + u'\u272f': '{\\ding{79}}', + u'\u2730': '{\\ding{80}}', + u'\u2731': '{\\ding{81}}', + u'\u2732': '{\\ding{82}}', + u'\u2733': '{\\ding{83}}', + u'\u2734': '{\\ding{84}}', + u'\u2735': '{\\ding{85}}', + u'\u2736': '{\\ding{86}}', + u'\u2737': '{\\ding{87}}', + u'\u2738': '{\\ding{88}}', + u'\u2739': '{\\ding{89}}', + u'\u273a': '{\\ding{90}}', + u'\u273b': '{\\ding{91}}', + u'\u273c': '{\\ding{92}}', + u'\u273d': '{\\ding{93}}', + u'\u273e': '{\\ding{94}}', + u'\u273f': '{\\ding{95}}', + u'\u2740': '{\\ding{96}}', + u'\u2741': '{\\ding{97}}', + u'\u2742': '{\\ding{98}}', + u'\u2743': '{\\ding{99}}', + u'\u2744': '{\\ding{100}}', + u'\u2745': '{\\ding{101}}', + u'\u2746': '{\\ding{102}}', + u'\u2747': '{\\ding{103}}', + u'\u2748': '{\\ding{104}}', + u'\u2749': '{\\ding{105}}', + u'\u274a': '{\\ding{106}}', + u'\u274b': '{\\ding{107}}', + u'\u274d': '{\\ding{109}}', + u'\u274f': '{\\ding{111}}', + u'\u2750': '{\\ding{112}}', + u'\u2751': '{\\ding{113}}', + u'\u2752': '{\\ding{114}}', + u'\u2756': '{\\ding{118}}', + u'\u2758': '{\\ding{120}}', + u'\u2759': '{\\ding{121}}', + u'\u275a': '{\\ding{122}}', + u'\u275b': '{\\ding{123}}', + u'\u275c': '{\\ding{124}}', + u'\u275d': '{\\ding{125}}', + u'\u275e': '{\\ding{126}}', + u'\u2761': '{\\ding{161}}', + u'\u2762': '{\\ding{162}}', + u'\u2763': '{\\ding{163}}', + u'\u2764': '{\\ding{164}}', + u'\u2765': '{\\ding{165}}', + u'\u2766': '{\\ding{166}}', + u'\u2767': '{\\ding{167}}', + u'\u2776': '{\\ding{182}}', + u'\u2777': '{\\ding{183}}', + u'\u2778': '{\\ding{184}}', + u'\u2779': '{\\ding{185}}', + u'\u277a': '{\\ding{186}}', + u'\u277b': '{\\ding{187}}', + u'\u277c': '{\\ding{188}}', + u'\u277d': '{\\ding{189}}', + u'\u277e': '{\\ding{190}}', + u'\u277f': '{\\ding{191}}', + u'\u2780': '{\\ding{192}}', + u'\u2781': '{\\ding{193}}', + u'\u2782': '{\\ding{194}}', + u'\u2783': '{\\ding{195}}', + u'\u2784': '{\\ding{196}}', + u'\u2785': '{\\ding{197}}', + u'\u2786': '{\\ding{198}}', + u'\u2787': '{\\ding{199}}', + u'\u2788': '{\\ding{200}}', + u'\u2789': '{\\ding{201}}', + u'\u278a': '{\\ding{202}}', + u'\u278b': '{\\ding{203}}', + u'\u278c': '{\\ding{204}}', + u'\u278d': '{\\ding{205}}', + u'\u278e': '{\\ding{206}}', + u'\u278f': '{\\ding{207}}', + u'\u2790': '{\\ding{208}}', + u'\u2791': '{\\ding{209}}', + u'\u2792': '{\\ding{210}}', + u'\u2793': '{\\ding{211}}', + u'\u2794': '{\\ding{212}}', + u'\u2798': '{\\ding{216}}', + u'\u2799': '{\\ding{217}}', + u'\u279a': '{\\ding{218}}', + u'\u279b': '{\\ding{219}}', + u'\u279c': '{\\ding{220}}', + u'\u279d': '{\\ding{221}}', + u'\u279e': '{\\ding{222}}', + u'\u279f': '{\\ding{223}}', + u'\u27a0': '{\\ding{224}}', + u'\u27a1': '{\\ding{225}}', + u'\u27a2': '{\\ding{226}}', + u'\u27a3': '{\\ding{227}}', + u'\u27a4': '{\\ding{228}}', + u'\u27a5': '{\\ding{229}}', + u'\u27a6': '{\\ding{230}}', + u'\u27a7': '{\\ding{231}}', + u'\u27a8': '{\\ding{232}}', + u'\u27a9': '{\\ding{233}}', + u'\u27aa': '{\\ding{234}}', + u'\u27ab': '{\\ding{235}}', + u'\u27ac': '{\\ding{236}}', + u'\u27ad': '{\\ding{237}}', + u'\u27ae': '{\\ding{238}}', + u'\u27af': '{\\ding{239}}', + u'\u27b1': '{\\ding{241}}', + u'\u27b2': '{\\ding{242}}', + u'\u27b3': '{\\ding{243}}', + u'\u27b4': '{\\ding{244}}', + u'\u27b5': '{\\ding{245}}', + u'\u27b6': '{\\ding{246}}', + u'\u27b7': '{\\ding{247}}', + u'\u27b8': '{\\ding{248}}', + u'\u27b9': '{\\ding{249}}', + u'\u27ba': '{\\ding{250}}', + u'\u27bb': '{\\ding{251}}', + u'\u27bc': '{\\ding{252}}', + u'\u27bd': '{\\ding{253}}', + u'\u27be': '{\\ding{254}}', + u'\u27f5': '$\\longleftarrow$', + u'\u27f6': '$\\longrightarrow$', + u'\u27f7': '$\\longleftrightarrow$', + u'\u27f8': '$\\Longleftarrow$', + u'\u27f9': '$\\Longrightarrow$', + u'\u27fa': '$\\Longleftrightarrow$', + u'\u27fc': '$\\longmapsto$', + u'\u27ff': '$\\sim\\joinrel\\leadsto$', + u'\u2905': '$\\ElsevierGlyph{E212}$', + u'\u2912': '$\\UpArrowBar$', + u'\u2913': '$\\DownArrowBar$', + u'\u2923': '$\\ElsevierGlyph{E20C}$', + u'\u2924': '$\\ElsevierGlyph{E20D}$', + u'\u2925': '$\\ElsevierGlyph{E20B}$', + u'\u2926': '$\\ElsevierGlyph{E20A}$', + u'\u2927': '$\\ElsevierGlyph{E211}$', + u'\u2928': '$\\ElsevierGlyph{E20E}$', + u'\u2929': '$\\ElsevierGlyph{E20F}$', + u'\u292a': '$\\ElsevierGlyph{E210}$', + u'\u2933': '$\\ElsevierGlyph{E21C}$', + u'\u2936': '$\\ElsevierGlyph{E21A}$', + u'\u2937': '$\\ElsevierGlyph{E219}$', + u'\u2940': '$\\Elolarr$', + u'\u2941': '$\\Elorarr$', + u'\u2942': '$\\ElzRlarr$', + u'\u2944': '$\\ElzrLarr$', + u'\u2947': '$\\Elzrarrx$', + u'\u294e': '$\\LeftRightVector$', + u'\u294f': '$\\RightUpDownVector$', + u'\u2950': '$\\DownLeftRightVector$', + u'\u2951': '$\\LeftUpDownVector$', + u'\u2952': '$\\LeftVectorBar$', + u'\u2953': '$\\RightVectorBar$', + u'\u2954': '$\\RightUpVectorBar$', + u'\u2955': '$\\RightDownVectorBar$', + u'\u2956': '$\\DownLeftVectorBar$', + u'\u2957': '$\\DownRightVectorBar$', + u'\u2958': '$\\LeftUpVectorBar$', + u'\u2959': '$\\LeftDownVectorBar$', + u'\u295a': '$\\LeftTeeVector$', + u'\u295b': '$\\RightTeeVector$', + u'\u295c': '$\\RightUpTeeVector$', + u'\u295d': '$\\RightDownTeeVector$', + u'\u295e': '$\\DownLeftTeeVector$', + u'\u295f': '$\\DownRightTeeVector$', + u'\u2960': '$\\LeftUpTeeVector$', + u'\u2961': '$\\LeftDownTeeVector$', + u'\u296e': '$\\UpEquilibrium$', + u'\u296f': '$\\ReverseUpEquilibrium$', + u'\u2970': '$\\RoundImplies$', + u'\u297c': '$\\ElsevierGlyph{E214}$', + u'\u297d': '$\\ElsevierGlyph{E215}$', + u'\u2980': '$\\Elztfnc$', + u'\u2985': '$\\ElsevierGlyph{3018}$', + u'\u2986': '$\\Elroang$', + u'\u2993': '$<\\kern-0.58em($', + u'\u2994': '$\\ElsevierGlyph{E291}$', + u'\u2999': '$\\Elzddfnc$', + u'\u299c': '$\\Angle$', + u'\u29a0': '$\\Elzlpargt$', + u'\u29b5': '$\\ElsevierGlyph{E260}$', + u'\u29b6': '$\\ElsevierGlyph{E61B}$', + u'\u29ca': '$\\ElzLap$', + u'\u29cb': '$\\Elzdefas$', + u'\u29cf': '$\\LeftTriangleBar$', + u'\u29d0': '$\\RightTriangleBar$', + u'\u29dc': '$\\ElsevierGlyph{E372}$', + u'\u29eb': '$\\blacklozenge$', + u'\u29f4': '$\\RuleDelayed$', + u'\u2a04': '$\\Elxuplus$', + u'\u2a05': '$\\ElzThr$', + u'\u2a06': '$\\Elxsqcup$', + u'\u2a07': '$\\ElzInf$', + u'\u2a08': '$\\ElzSup$', + u'\u2a0d': '$\\ElzCint$', + u'\u2a0f': '$\\clockoint$', + u'\u2a10': '$\\ElsevierGlyph{E395}$', + u'\u2a16': '$\\sqrint$', + u'\u2a25': '$\\ElsevierGlyph{E25A}$', + u'\u2a2a': '$\\ElsevierGlyph{E25B}$', + u'\u2a2d': '$\\ElsevierGlyph{E25C}$', + u'\u2a2e': '$\\ElsevierGlyph{E25D}$', + u'\u2a2f': '$\\ElzTimes$', + u'\u2a34': '$\\ElsevierGlyph{E25E}$', + u'\u2a35': '$\\ElsevierGlyph{E25E}$', + u'\u2a3c': '$\\ElsevierGlyph{E259}$', + u'\u2a3f': '$\\amalg$', + u'\u2a53': '$\\ElzAnd$', + u'\u2a54': '$\\ElzOr$', + u'\u2a55': '$\\ElsevierGlyph{E36E}$', + u'\u2a56': '$\\ElOr$', + u'\u2a5e': '$\\perspcorrespond$', + u'\u2a5f': '$\\Elzminhat$', + u'\u2a63': '$\\ElsevierGlyph{225A}$', + u'\u2a6e': '$\\stackrel{*}{=}$', + u'\u2a75': '$\\Equal$', + u'\u2a7d': '$\\leqslant$', + u'\u2a7e': '$\\geqslant$', + u'\u2a85': '$\\lessapprox$', + u'\u2a86': '$\\gtrapprox$', + u'\u2a87': '$\\lneq$', + u'\u2a88': '$\\gneq$', + u'\u2a89': '$\\lnapprox$', + u'\u2a8a': '$\\gnapprox$', + u'\u2a8b': '$\\lesseqqgtr$', + u'\u2a8c': '$\\gtreqqless$', + u'\u2a95': '$\\eqslantless$', + u'\u2a96': '$\\eqslantgtr$', + u'\u2a9d': '$\\Pisymbol{ppi020}{117}$', + u'\u2a9e': '$\\Pisymbol{ppi020}{105}$', + u'\u2aa1': '$\\NestedLessLess$', + u'\u2aa2': '$\\NestedGreaterGreater$', + u'\u2aaf': '$\\preceq$', + u'\u2ab0': '$\\succeq$', + u'\u2ab5': '$\\precneqq$', + u'\u2ab6': '$\\succneqq$', + u'\u2ab7': '$\\precapprox$', + u'\u2ab8': '$\\succapprox$', + u'\u2ab9': '$\\precnapprox$', + u'\u2aba': '$\\succnapprox$', + u'\u2ac5': '$\\subseteqq$', + u'\u2ac6': '$\\supseteqq$', + u'\u2acb': '$\\subsetneqq$', + u'\u2acc': '$\\supsetneqq$', + u'\u2aeb': '$\\ElsevierGlyph{E30D}$', + u'\u2af6': '$\\Elztdcol$', + u'\u2afd': '${{/}\\!\\!{/}}$', + u'\u300a': '$\\ElsevierGlyph{300A}$', + u'\u300b': '$\\ElsevierGlyph{300B}$', + u'\u3018': '$\\ElsevierGlyph{3018}$', + u'\u3019': '$\\ElsevierGlyph{3019}$', + u'\u301a': '$\\openbracketleft$', + u'\u301b': '$\\openbracketright$', + u'\ufb00': '{ff}', + u'\ufb01': '{fi}', + u'\ufb02': '{fl}', + u'\ufb03': '{ffi}', + u'\ufb04': '{ffl}', + u'\U0001d400': '$\\mathbf{A}$', + u'\U0001d401': '$\\mathbf{B}$', + u'\U0001d402': '$\\mathbf{C}$', + u'\U0001d403': '$\\mathbf{D}$', + u'\U0001d404': '$\\mathbf{E}$', + u'\U0001d405': '$\\mathbf{F}$', + u'\U0001d406': '$\\mathbf{G}$', + u'\U0001d407': '$\\mathbf{H}$', + u'\U0001d408': '$\\mathbf{I}$', + u'\U0001d409': '$\\mathbf{J}$', + u'\U0001d40a': '$\\mathbf{K}$', + u'\U0001d40b': '$\\mathbf{L}$', + u'\U0001d40c': '$\\mathbf{M}$', + u'\U0001d40d': '$\\mathbf{N}$', + u'\U0001d40e': '$\\mathbf{O}$', + u'\U0001d40f': '$\\mathbf{P}$', + u'\U0001d410': '$\\mathbf{Q}$', + u'\U0001d411': '$\\mathbf{R}$', + u'\U0001d412': '$\\mathbf{S}$', + u'\U0001d413': '$\\mathbf{T}$', + u'\U0001d414': '$\\mathbf{U}$', + u'\U0001d415': '$\\mathbf{V}$', + u'\U0001d416': '$\\mathbf{W}$', + u'\U0001d417': '$\\mathbf{X}$', + u'\U0001d418': '$\\mathbf{Y}$', + u'\U0001d419': '$\\mathbf{Z}$', + u'\U0001d41a': '$\\mathbf{a}$', + u'\U0001d41b': '$\\mathbf{b}$', + u'\U0001d41c': '$\\mathbf{c}$', + u'\U0001d41d': '$\\mathbf{d}$', + u'\U0001d41e': '$\\mathbf{e}$', + u'\U0001d41f': '$\\mathbf{f}$', + u'\U0001d420': '$\\mathbf{g}$', + u'\U0001d421': '$\\mathbf{h}$', + u'\U0001d422': '$\\mathbf{i}$', + u'\U0001d423': '$\\mathbf{j}$', + u'\U0001d424': '$\\mathbf{k}$', + u'\U0001d425': '$\\mathbf{l}$', + u'\U0001d426': '$\\mathbf{m}$', + u'\U0001d427': '$\\mathbf{n}$', + u'\U0001d428': '$\\mathbf{o}$', + u'\U0001d429': '$\\mathbf{p}$', + u'\U0001d42a': '$\\mathbf{q}$', + u'\U0001d42b': '$\\mathbf{r}$', + u'\U0001d42c': '$\\mathbf{s}$', + u'\U0001d42d': '$\\mathbf{t}$', + u'\U0001d42e': '$\\mathbf{u}$', + u'\U0001d42f': '$\\mathbf{v}$', + u'\U0001d430': '$\\mathbf{w}$', + u'\U0001d431': '$\\mathbf{x}$', + u'\U0001d432': '$\\mathbf{y}$', + u'\U0001d433': '$\\mathbf{z}$', + u'\U0001d434': '$\\mathsl{A}$', + u'\U0001d435': '$\\mathsl{B}$', + u'\U0001d436': '$\\mathsl{C}$', + u'\U0001d437': '$\\mathsl{D}$', + u'\U0001d438': '$\\mathsl{E}$', + u'\U0001d439': '$\\mathsl{F}$', + u'\U0001d43a': '$\\mathsl{G}$', + u'\U0001d43b': '$\\mathsl{H}$', + u'\U0001d43c': '$\\mathsl{I}$', + u'\U0001d43d': '$\\mathsl{J}$', + u'\U0001d43e': '$\\mathsl{K}$', + u'\U0001d43f': '$\\mathsl{L}$', + u'\U0001d440': '$\\mathsl{M}$', + u'\U0001d441': '$\\mathsl{N}$', + u'\U0001d442': '$\\mathsl{O}$', + u'\U0001d443': '$\\mathsl{P}$', + u'\U0001d444': '$\\mathsl{Q}$', + u'\U0001d445': '$\\mathsl{R}$', + u'\U0001d446': '$\\mathsl{S}$', + u'\U0001d447': '$\\mathsl{T}$', + u'\U0001d448': '$\\mathsl{U}$', + u'\U0001d449': '$\\mathsl{V}$', + u'\U0001d44a': '$\\mathsl{W}$', + u'\U0001d44b': '$\\mathsl{X}$', + u'\U0001d44c': '$\\mathsl{Y}$', + u'\U0001d44d': '$\\mathsl{Z}$', + u'\U0001d44e': '$\\mathsl{a}$', + u'\U0001d44f': '$\\mathsl{b}$', + u'\U0001d450': '$\\mathsl{c}$', + u'\U0001d451': '$\\mathsl{d}$', + u'\U0001d452': '$\\mathsl{e}$', + u'\U0001d453': '$\\mathsl{f}$', + u'\U0001d454': '$\\mathsl{g}$', + u'\U0001d456': '$\\mathsl{i}$', + u'\U0001d457': '$\\mathsl{j}$', + u'\U0001d458': '$\\mathsl{k}$', + u'\U0001d459': '$\\mathsl{l}$', + u'\U0001d45a': '$\\mathsl{m}$', + u'\U0001d45b': '$\\mathsl{n}$', + u'\U0001d45c': '$\\mathsl{o}$', + u'\U0001d45d': '$\\mathsl{p}$', + u'\U0001d45e': '$\\mathsl{q}$', + u'\U0001d45f': '$\\mathsl{r}$', + u'\U0001d460': '$\\mathsl{s}$', + u'\U0001d461': '$\\mathsl{t}$', + u'\U0001d462': '$\\mathsl{u}$', + u'\U0001d463': '$\\mathsl{v}$', + u'\U0001d464': '$\\mathsl{w}$', + u'\U0001d465': '$\\mathsl{x}$', + u'\U0001d466': '$\\mathsl{y}$', + u'\U0001d467': '$\\mathsl{z}$', + u'\U0001d468': '$\\mathbit{A}$', + u'\U0001d469': '$\\mathbit{B}$', + u'\U0001d46a': '$\\mathbit{C}$', + u'\U0001d46b': '$\\mathbit{D}$', + u'\U0001d46c': '$\\mathbit{E}$', + u'\U0001d46d': '$\\mathbit{F}$', + u'\U0001d46e': '$\\mathbit{G}$', + u'\U0001d46f': '$\\mathbit{H}$', + u'\U0001d470': '$\\mathbit{I}$', + u'\U0001d471': '$\\mathbit{J}$', + u'\U0001d472': '$\\mathbit{K}$', + u'\U0001d473': '$\\mathbit{L}$', + u'\U0001d474': '$\\mathbit{M}$', + u'\U0001d475': '$\\mathbit{N}$', + u'\U0001d476': '$\\mathbit{O}$', + u'\U0001d477': '$\\mathbit{P}$', + u'\U0001d478': '$\\mathbit{Q}$', + u'\U0001d479': '$\\mathbit{R}$', + u'\U0001d47a': '$\\mathbit{S}$', + u'\U0001d47b': '$\\mathbit{T}$', + u'\U0001d47c': '$\\mathbit{U}$', + u'\U0001d47d': '$\\mathbit{V}$', + u'\U0001d47e': '$\\mathbit{W}$', + u'\U0001d47f': '$\\mathbit{X}$', + u'\U0001d480': '$\\mathbit{Y}$', + u'\U0001d481': '$\\mathbit{Z}$', + u'\U0001d482': '$\\mathbit{a}$', + u'\U0001d483': '$\\mathbit{b}$', + u'\U0001d484': '$\\mathbit{c}$', + u'\U0001d485': '$\\mathbit{d}$', + u'\U0001d486': '$\\mathbit{e}$', + u'\U0001d487': '$\\mathbit{f}$', + u'\U0001d488': '$\\mathbit{g}$', + u'\U0001d489': '$\\mathbit{h}$', + u'\U0001d48a': '$\\mathbit{i}$', + u'\U0001d48b': '$\\mathbit{j}$', + u'\U0001d48c': '$\\mathbit{k}$', + u'\U0001d48d': '$\\mathbit{l}$', + u'\U0001d48e': '$\\mathbit{m}$', + u'\U0001d48f': '$\\mathbit{n}$', + u'\U0001d490': '$\\mathbit{o}$', + u'\U0001d491': '$\\mathbit{p}$', + u'\U0001d492': '$\\mathbit{q}$', + u'\U0001d493': '$\\mathbit{r}$', + u'\U0001d494': '$\\mathbit{s}$', + u'\U0001d495': '$\\mathbit{t}$', + u'\U0001d496': '$\\mathbit{u}$', + u'\U0001d497': '$\\mathbit{v}$', + u'\U0001d498': '$\\mathbit{w}$', + u'\U0001d499': '$\\mathbit{x}$', + u'\U0001d49a': '$\\mathbit{y}$', + u'\U0001d49b': '$\\mathbit{z}$', + u'\U0001d49c': '$\\mathscr{A}$', + u'\U0001d49e': '$\\mathscr{C}$', + u'\U0001d49f': '$\\mathscr{D}$', + u'\U0001d4a2': '$\\mathscr{G}$', + u'\U0001d4a5': '$\\mathscr{J}$', + u'\U0001d4a6': '$\\mathscr{K}$', + u'\U0001d4a9': '$\\mathscr{N}$', + u'\U0001d4aa': '$\\mathscr{O}$', + u'\U0001d4ab': '$\\mathscr{P}$', + u'\U0001d4ac': '$\\mathscr{Q}$', + u'\U0001d4ae': '$\\mathscr{S}$', + u'\U0001d4af': '$\\mathscr{T}$', + u'\U0001d4b0': '$\\mathscr{U}$', + u'\U0001d4b1': '$\\mathscr{V}$', + u'\U0001d4b2': '$\\mathscr{W}$', + u'\U0001d4b3': '$\\mathscr{X}$', + u'\U0001d4b4': '$\\mathscr{Y}$', + u'\U0001d4b5': '$\\mathscr{Z}$', + u'\U0001d4b6': '$\\mathscr{a}$', + u'\U0001d4b7': '$\\mathscr{b}$', + u'\U0001d4b8': '$\\mathscr{c}$', + u'\U0001d4b9': '$\\mathscr{d}$', + u'\U0001d4bb': '$\\mathscr{f}$', + u'\U0001d4bd': '$\\mathscr{h}$', + u'\U0001d4be': '$\\mathscr{i}$', + u'\U0001d4bf': '$\\mathscr{j}$', + u'\U0001d4c0': '$\\mathscr{k}$', + u'\U0001d4c1': '$\\mathscr{l}$', + u'\U0001d4c2': '$\\mathscr{m}$', + u'\U0001d4c3': '$\\mathscr{n}$', + u'\U0001d4c5': '$\\mathscr{p}$', + u'\U0001d4c6': '$\\mathscr{q}$', + u'\U0001d4c7': '$\\mathscr{r}$', + u'\U0001d4c8': '$\\mathscr{s}$', + u'\U0001d4c9': '$\\mathscr{t}$', + u'\U0001d4ca': '$\\mathscr{u}$', + u'\U0001d4cb': '$\\mathscr{v}$', + u'\U0001d4cc': '$\\mathscr{w}$', + u'\U0001d4cd': '$\\mathscr{x}$', + u'\U0001d4ce': '$\\mathscr{y}$', + u'\U0001d4cf': '$\\mathscr{z}$', + u'\U0001d4d0': '$\\mathmit{A}$', + u'\U0001d4d1': '$\\mathmit{B}$', + u'\U0001d4d2': '$\\mathmit{C}$', + u'\U0001d4d3': '$\\mathmit{D}$', + u'\U0001d4d4': '$\\mathmit{E}$', + u'\U0001d4d5': '$\\mathmit{F}$', + u'\U0001d4d6': '$\\mathmit{G}$', + u'\U0001d4d7': '$\\mathmit{H}$', + u'\U0001d4d8': '$\\mathmit{I}$', + u'\U0001d4d9': '$\\mathmit{J}$', + u'\U0001d4da': '$\\mathmit{K}$', + u'\U0001d4db': '$\\mathmit{L}$', + u'\U0001d4dc': '$\\mathmit{M}$', + u'\U0001d4dd': '$\\mathmit{N}$', + u'\U0001d4de': '$\\mathmit{O}$', + u'\U0001d4df': '$\\mathmit{P}$', + u'\U0001d4e0': '$\\mathmit{Q}$', + u'\U0001d4e1': '$\\mathmit{R}$', + u'\U0001d4e2': '$\\mathmit{S}$', + u'\U0001d4e3': '$\\mathmit{T}$', + u'\U0001d4e4': '$\\mathmit{U}$', + u'\U0001d4e5': '$\\mathmit{V}$', + u'\U0001d4e6': '$\\mathmit{W}$', + u'\U0001d4e7': '$\\mathmit{X}$', + u'\U0001d4e8': '$\\mathmit{Y}$', + u'\U0001d4e9': '$\\mathmit{Z}$', + u'\U0001d4ea': '$\\mathmit{a}$', + u'\U0001d4eb': '$\\mathmit{b}$', + u'\U0001d4ec': '$\\mathmit{c}$', + u'\U0001d4ed': '$\\mathmit{d}$', + u'\U0001d4ee': '$\\mathmit{e}$', + u'\U0001d4ef': '$\\mathmit{f}$', + u'\U0001d4f0': '$\\mathmit{g}$', + u'\U0001d4f1': '$\\mathmit{h}$', + u'\U0001d4f2': '$\\mathmit{i}$', + u'\U0001d4f3': '$\\mathmit{j}$', + u'\U0001d4f4': '$\\mathmit{k}$', + u'\U0001d4f5': '$\\mathmit{l}$', + u'\U0001d4f6': '$\\mathmit{m}$', + u'\U0001d4f7': '$\\mathmit{n}$', + u'\U0001d4f8': '$\\mathmit{o}$', + u'\U0001d4f9': '$\\mathmit{p}$', + u'\U0001d4fa': '$\\mathmit{q}$', + u'\U0001d4fb': '$\\mathmit{r}$', + u'\U0001d4fc': '$\\mathmit{s}$', + u'\U0001d4fd': '$\\mathmit{t}$', + u'\U0001d4fe': '$\\mathmit{u}$', + u'\U0001d4ff': '$\\mathmit{v}$', + u'\U0001d500': '$\\mathmit{w}$', + u'\U0001d501': '$\\mathmit{x}$', + u'\U0001d502': '$\\mathmit{y}$', + u'\U0001d503': '$\\mathmit{z}$', + u'\U0001d504': '$\\mathfrak{A}$', + u'\U0001d505': '$\\mathfrak{B}$', + u'\U0001d507': '$\\mathfrak{D}$', + u'\U0001d508': '$\\mathfrak{E}$', + u'\U0001d509': '$\\mathfrak{F}$', + u'\U0001d50a': '$\\mathfrak{G}$', + u'\U0001d50d': '$\\mathfrak{J}$', + u'\U0001d50e': '$\\mathfrak{K}$', + u'\U0001d50f': '$\\mathfrak{L}$', + u'\U0001d510': '$\\mathfrak{M}$', + u'\U0001d511': '$\\mathfrak{N}$', + u'\U0001d512': '$\\mathfrak{O}$', + u'\U0001d513': '$\\mathfrak{P}$', + u'\U0001d514': '$\\mathfrak{Q}$', + u'\U0001d516': '$\\mathfrak{S}$', + u'\U0001d517': '$\\mathfrak{T}$', + u'\U0001d518': '$\\mathfrak{U}$', + u'\U0001d519': '$\\mathfrak{V}$', + u'\U0001d51a': '$\\mathfrak{W}$', + u'\U0001d51b': '$\\mathfrak{X}$', + u'\U0001d51c': '$\\mathfrak{Y}$', + u'\U0001d51e': '$\\mathfrak{a}$', + u'\U0001d51f': '$\\mathfrak{b}$', + u'\U0001d520': '$\\mathfrak{c}$', + u'\U0001d521': '$\\mathfrak{d}$', + u'\U0001d522': '$\\mathfrak{e}$', + u'\U0001d523': '$\\mathfrak{f}$', + u'\U0001d524': '$\\mathfrak{g}$', + u'\U0001d525': '$\\mathfrak{h}$', + u'\U0001d526': '$\\mathfrak{i}$', + u'\U0001d527': '$\\mathfrak{j}$', + u'\U0001d528': '$\\mathfrak{k}$', + u'\U0001d529': '$\\mathfrak{l}$', + u'\U0001d52a': '$\\mathfrak{m}$', + u'\U0001d52b': '$\\mathfrak{n}$', + u'\U0001d52c': '$\\mathfrak{o}$', + u'\U0001d52d': '$\\mathfrak{p}$', + u'\U0001d52e': '$\\mathfrak{q}$', + u'\U0001d52f': '$\\mathfrak{r}$', + u'\U0001d530': '$\\mathfrak{s}$', + u'\U0001d531': '$\\mathfrak{t}$', + u'\U0001d532': '$\\mathfrak{u}$', + u'\U0001d533': '$\\mathfrak{v}$', + u'\U0001d534': '$\\mathfrak{w}$', + u'\U0001d535': '$\\mathfrak{x}$', + u'\U0001d536': '$\\mathfrak{y}$', + u'\U0001d537': '$\\mathfrak{z}$', + u'\U0001d538': '$\\mathbb{A}$', + u'\U0001d539': '$\\mathbb{B}$', + u'\U0001d53b': '$\\mathbb{D}$', + u'\U0001d53c': '$\\mathbb{E}$', + u'\U0001d53d': '$\\mathbb{F}$', + u'\U0001d53e': '$\\mathbb{G}$', + u'\U0001d540': '$\\mathbb{I}$', + u'\U0001d541': '$\\mathbb{J}$', + u'\U0001d542': '$\\mathbb{K}$', + u'\U0001d543': '$\\mathbb{L}$', + u'\U0001d544': '$\\mathbb{M}$', + u'\U0001d546': '$\\mathbb{O}$', + u'\U0001d54a': '$\\mathbb{S}$', + u'\U0001d54b': '$\\mathbb{T}$', + u'\U0001d54c': '$\\mathbb{U}$', + u'\U0001d54d': '$\\mathbb{V}$', + u'\U0001d54e': '$\\mathbb{W}$', + u'\U0001d54f': '$\\mathbb{X}$', + u'\U0001d550': '$\\mathbb{Y}$', + u'\U0001d552': '$\\mathbb{a}$', + u'\U0001d553': '$\\mathbb{b}$', + u'\U0001d554': '$\\mathbb{c}$', + u'\U0001d555': '$\\mathbb{d}$', + u'\U0001d556': '$\\mathbb{e}$', + u'\U0001d557': '$\\mathbb{f}$', + u'\U0001d558': '$\\mathbb{g}$', + u'\U0001d559': '$\\mathbb{h}$', + u'\U0001d55a': '$\\mathbb{i}$', + u'\U0001d55b': '$\\mathbb{j}$', + u'\U0001d55c': '$\\mathbb{k}$', + u'\U0001d55d': '$\\mathbb{l}$', + u'\U0001d55e': '$\\mathbb{m}$', + u'\U0001d55f': '$\\mathbb{n}$', + u'\U0001d560': '$\\mathbb{o}$', + u'\U0001d561': '$\\mathbb{p}$', + u'\U0001d562': '$\\mathbb{q}$', + u'\U0001d563': '$\\mathbb{r}$', + u'\U0001d564': '$\\mathbb{s}$', + u'\U0001d565': '$\\mathbb{t}$', + u'\U0001d566': '$\\mathbb{u}$', + u'\U0001d567': '$\\mathbb{v}$', + u'\U0001d568': '$\\mathbb{w}$', + u'\U0001d569': '$\\mathbb{x}$', + u'\U0001d56a': '$\\mathbb{y}$', + u'\U0001d56b': '$\\mathbb{z}$', + u'\U0001d56c': '$\\mathslbb{A}$', + u'\U0001d56d': '$\\mathslbb{B}$', + u'\U0001d56e': '$\\mathslbb{C}$', + u'\U0001d56f': '$\\mathslbb{D}$', + u'\U0001d570': '$\\mathslbb{E}$', + u'\U0001d571': '$\\mathslbb{F}$', + u'\U0001d572': '$\\mathslbb{G}$', + u'\U0001d573': '$\\mathslbb{H}$', + u'\U0001d574': '$\\mathslbb{I}$', + u'\U0001d575': '$\\mathslbb{J}$', + u'\U0001d576': '$\\mathslbb{K}$', + u'\U0001d577': '$\\mathslbb{L}$', + u'\U0001d578': '$\\mathslbb{M}$', + u'\U0001d579': '$\\mathslbb{N}$', + u'\U0001d57a': '$\\mathslbb{O}$', + u'\U0001d57b': '$\\mathslbb{P}$', + u'\U0001d57c': '$\\mathslbb{Q}$', + u'\U0001d57d': '$\\mathslbb{R}$', + u'\U0001d57e': '$\\mathslbb{S}$', + u'\U0001d57f': '$\\mathslbb{T}$', + u'\U0001d580': '$\\mathslbb{U}$', + u'\U0001d581': '$\\mathslbb{V}$', + u'\U0001d582': '$\\mathslbb{W}$', + u'\U0001d583': '$\\mathslbb{X}$', + u'\U0001d584': '$\\mathslbb{Y}$', + u'\U0001d585': '$\\mathslbb{Z}$', + u'\U0001d586': '$\\mathslbb{a}$', + u'\U0001d587': '$\\mathslbb{b}$', + u'\U0001d588': '$\\mathslbb{c}$', + u'\U0001d589': '$\\mathslbb{d}$', + u'\U0001d58a': '$\\mathslbb{e}$', + u'\U0001d58b': '$\\mathslbb{f}$', + u'\U0001d58c': '$\\mathslbb{g}$', + u'\U0001d58d': '$\\mathslbb{h}$', + u'\U0001d58e': '$\\mathslbb{i}$', + u'\U0001d58f': '$\\mathslbb{j}$', + u'\U0001d590': '$\\mathslbb{k}$', + u'\U0001d591': '$\\mathslbb{l}$', + u'\U0001d592': '$\\mathslbb{m}$', + u'\U0001d593': '$\\mathslbb{n}$', + u'\U0001d594': '$\\mathslbb{o}$', + u'\U0001d595': '$\\mathslbb{p}$', + u'\U0001d596': '$\\mathslbb{q}$', + u'\U0001d597': '$\\mathslbb{r}$', + u'\U0001d598': '$\\mathslbb{s}$', + u'\U0001d599': '$\\mathslbb{t}$', + u'\U0001d59a': '$\\mathslbb{u}$', + u'\U0001d59b': '$\\mathslbb{v}$', + u'\U0001d59c': '$\\mathslbb{w}$', + u'\U0001d59d': '$\\mathslbb{x}$', + u'\U0001d59e': '$\\mathslbb{y}$', + u'\U0001d59f': '$\\mathslbb{z}$', + u'\U0001d5a0': '$\\mathsf{A}$', + u'\U0001d5a1': '$\\mathsf{B}$', + u'\U0001d5a2': '$\\mathsf{C}$', + u'\U0001d5a3': '$\\mathsf{D}$', + u'\U0001d5a4': '$\\mathsf{E}$', + u'\U0001d5a5': '$\\mathsf{F}$', + u'\U0001d5a6': '$\\mathsf{G}$', + u'\U0001d5a7': '$\\mathsf{H}$', + u'\U0001d5a8': '$\\mathsf{I}$', + u'\U0001d5a9': '$\\mathsf{J}$', + u'\U0001d5aa': '$\\mathsf{K}$', + u'\U0001d5ab': '$\\mathsf{L}$', + u'\U0001d5ac': '$\\mathsf{M}$', + u'\U0001d5ad': '$\\mathsf{N}$', + u'\U0001d5ae': '$\\mathsf{O}$', + u'\U0001d5af': '$\\mathsf{P}$', + u'\U0001d5b0': '$\\mathsf{Q}$', + u'\U0001d5b1': '$\\mathsf{R}$', + u'\U0001d5b2': '$\\mathsf{S}$', + u'\U0001d5b3': '$\\mathsf{T}$', + u'\U0001d5b4': '$\\mathsf{U}$', + u'\U0001d5b5': '$\\mathsf{V}$', + u'\U0001d5b6': '$\\mathsf{W}$', + u'\U0001d5b7': '$\\mathsf{X}$', + u'\U0001d5b8': '$\\mathsf{Y}$', + u'\U0001d5b9': '$\\mathsf{Z}$', + u'\U0001d5ba': '$\\mathsf{a}$', + u'\U0001d5bb': '$\\mathsf{b}$', + u'\U0001d5bc': '$\\mathsf{c}$', + u'\U0001d5bd': '$\\mathsf{d}$', + u'\U0001d5be': '$\\mathsf{e}$', + u'\U0001d5bf': '$\\mathsf{f}$', + u'\U0001d5c0': '$\\mathsf{g}$', + u'\U0001d5c1': '$\\mathsf{h}$', + u'\U0001d5c2': '$\\mathsf{i}$', + u'\U0001d5c3': '$\\mathsf{j}$', + u'\U0001d5c4': '$\\mathsf{k}$', + u'\U0001d5c5': '$\\mathsf{l}$', + u'\U0001d5c6': '$\\mathsf{m}$', + u'\U0001d5c7': '$\\mathsf{n}$', + u'\U0001d5c8': '$\\mathsf{o}$', + u'\U0001d5c9': '$\\mathsf{p}$', + u'\U0001d5ca': '$\\mathsf{q}$', + u'\U0001d5cb': '$\\mathsf{r}$', + u'\U0001d5cc': '$\\mathsf{s}$', + u'\U0001d5cd': '$\\mathsf{t}$', + u'\U0001d5ce': '$\\mathsf{u}$', + u'\U0001d5cf': '$\\mathsf{v}$', + u'\U0001d5d0': '$\\mathsf{w}$', + u'\U0001d5d1': '$\\mathsf{x}$', + u'\U0001d5d2': '$\\mathsf{y}$', + u'\U0001d5d3': '$\\mathsf{z}$', + u'\U0001d5d4': '$\\mathsfbf{A}$', + u'\U0001d5d5': '$\\mathsfbf{B}$', + u'\U0001d5d6': '$\\mathsfbf{C}$', + u'\U0001d5d7': '$\\mathsfbf{D}$', + u'\U0001d5d8': '$\\mathsfbf{E}$', + u'\U0001d5d9': '$\\mathsfbf{F}$', + u'\U0001d5da': '$\\mathsfbf{G}$', + u'\U0001d5db': '$\\mathsfbf{H}$', + u'\U0001d5dc': '$\\mathsfbf{I}$', + u'\U0001d5dd': '$\\mathsfbf{J}$', + u'\U0001d5de': '$\\mathsfbf{K}$', + u'\U0001d5df': '$\\mathsfbf{L}$', + u'\U0001d5e0': '$\\mathsfbf{M}$', + u'\U0001d5e1': '$\\mathsfbf{N}$', + u'\U0001d5e2': '$\\mathsfbf{O}$', + u'\U0001d5e3': '$\\mathsfbf{P}$', + u'\U0001d5e4': '$\\mathsfbf{Q}$', + u'\U0001d5e5': '$\\mathsfbf{R}$', + u'\U0001d5e6': '$\\mathsfbf{S}$', + u'\U0001d5e7': '$\\mathsfbf{T}$', + u'\U0001d5e8': '$\\mathsfbf{U}$', + u'\U0001d5e9': '$\\mathsfbf{V}$', + u'\U0001d5ea': '$\\mathsfbf{W}$', + u'\U0001d5eb': '$\\mathsfbf{X}$', + u'\U0001d5ec': '$\\mathsfbf{Y}$', + u'\U0001d5ed': '$\\mathsfbf{Z}$', + u'\U0001d5ee': '$\\mathsfbf{a}$', + u'\U0001d5ef': '$\\mathsfbf{b}$', + u'\U0001d5f0': '$\\mathsfbf{c}$', + u'\U0001d5f1': '$\\mathsfbf{d}$', + u'\U0001d5f2': '$\\mathsfbf{e}$', + u'\U0001d5f3': '$\\mathsfbf{f}$', + u'\U0001d5f4': '$\\mathsfbf{g}$', + u'\U0001d5f5': '$\\mathsfbf{h}$', + u'\U0001d5f6': '$\\mathsfbf{i}$', + u'\U0001d5f7': '$\\mathsfbf{j}$', + u'\U0001d5f8': '$\\mathsfbf{k}$', + u'\U0001d5f9': '$\\mathsfbf{l}$', + u'\U0001d5fa': '$\\mathsfbf{m}$', + u'\U0001d5fb': '$\\mathsfbf{n}$', + u'\U0001d5fc': '$\\mathsfbf{o}$', + u'\U0001d5fd': '$\\mathsfbf{p}$', + u'\U0001d5fe': '$\\mathsfbf{q}$', + u'\U0001d5ff': '$\\mathsfbf{r}$', + u'\U0001d600': '$\\mathsfbf{s}$', + u'\U0001d601': '$\\mathsfbf{t}$', + u'\U0001d602': '$\\mathsfbf{u}$', + u'\U0001d603': '$\\mathsfbf{v}$', + u'\U0001d604': '$\\mathsfbf{w}$', + u'\U0001d605': '$\\mathsfbf{x}$', + u'\U0001d606': '$\\mathsfbf{y}$', + u'\U0001d607': '$\\mathsfbf{z}$', + u'\U0001d608': '$\\mathsfsl{A}$', + u'\U0001d609': '$\\mathsfsl{B}$', + u'\U0001d60a': '$\\mathsfsl{C}$', + u'\U0001d60b': '$\\mathsfsl{D}$', + u'\U0001d60c': '$\\mathsfsl{E}$', + u'\U0001d60d': '$\\mathsfsl{F}$', + u'\U0001d60e': '$\\mathsfsl{G}$', + u'\U0001d60f': '$\\mathsfsl{H}$', + u'\U0001d610': '$\\mathsfsl{I}$', + u'\U0001d611': '$\\mathsfsl{J}$', + u'\U0001d612': '$\\mathsfsl{K}$', + u'\U0001d613': '$\\mathsfsl{L}$', + u'\U0001d614': '$\\mathsfsl{M}$', + u'\U0001d615': '$\\mathsfsl{N}$', + u'\U0001d616': '$\\mathsfsl{O}$', + u'\U0001d617': '$\\mathsfsl{P}$', + u'\U0001d618': '$\\mathsfsl{Q}$', + u'\U0001d619': '$\\mathsfsl{R}$', + u'\U0001d61a': '$\\mathsfsl{S}$', + u'\U0001d61b': '$\\mathsfsl{T}$', + u'\U0001d61c': '$\\mathsfsl{U}$', + u'\U0001d61d': '$\\mathsfsl{V}$', + u'\U0001d61e': '$\\mathsfsl{W}$', + u'\U0001d61f': '$\\mathsfsl{X}$', + u'\U0001d620': '$\\mathsfsl{Y}$', + u'\U0001d621': '$\\mathsfsl{Z}$', + u'\U0001d622': '$\\mathsfsl{a}$', + u'\U0001d623': '$\\mathsfsl{b}$', + u'\U0001d624': '$\\mathsfsl{c}$', + u'\U0001d625': '$\\mathsfsl{d}$', + u'\U0001d626': '$\\mathsfsl{e}$', + u'\U0001d627': '$\\mathsfsl{f}$', + u'\U0001d628': '$\\mathsfsl{g}$', + u'\U0001d629': '$\\mathsfsl{h}$', + u'\U0001d62a': '$\\mathsfsl{i}$', + u'\U0001d62b': '$\\mathsfsl{j}$', + u'\U0001d62c': '$\\mathsfsl{k}$', + u'\U0001d62d': '$\\mathsfsl{l}$', + u'\U0001d62e': '$\\mathsfsl{m}$', + u'\U0001d62f': '$\\mathsfsl{n}$', + u'\U0001d630': '$\\mathsfsl{o}$', + u'\U0001d631': '$\\mathsfsl{p}$', + u'\U0001d632': '$\\mathsfsl{q}$', + u'\U0001d633': '$\\mathsfsl{r}$', + u'\U0001d634': '$\\mathsfsl{s}$', + u'\U0001d635': '$\\mathsfsl{t}$', + u'\U0001d636': '$\\mathsfsl{u}$', + u'\U0001d637': '$\\mathsfsl{v}$', + u'\U0001d638': '$\\mathsfsl{w}$', + u'\U0001d639': '$\\mathsfsl{x}$', + u'\U0001d63a': '$\\mathsfsl{y}$', + u'\U0001d63b': '$\\mathsfsl{z}$', + u'\U0001d63c': '$\\mathsfbfsl{A}$', + u'\U0001d63d': '$\\mathsfbfsl{B}$', + u'\U0001d63e': '$\\mathsfbfsl{C}$', + u'\U0001d63f': '$\\mathsfbfsl{D}$', + u'\U0001d640': '$\\mathsfbfsl{E}$', + u'\U0001d641': '$\\mathsfbfsl{F}$', + u'\U0001d642': '$\\mathsfbfsl{G}$', + u'\U0001d643': '$\\mathsfbfsl{H}$', + u'\U0001d644': '$\\mathsfbfsl{I}$', + u'\U0001d645': '$\\mathsfbfsl{J}$', + u'\U0001d646': '$\\mathsfbfsl{K}$', + u'\U0001d647': '$\\mathsfbfsl{L}$', + u'\U0001d648': '$\\mathsfbfsl{M}$', + u'\U0001d649': '$\\mathsfbfsl{N}$', + u'\U0001d64a': '$\\mathsfbfsl{O}$', + u'\U0001d64b': '$\\mathsfbfsl{P}$', + u'\U0001d64c': '$\\mathsfbfsl{Q}$', + u'\U0001d64d': '$\\mathsfbfsl{R}$', + u'\U0001d64e': '$\\mathsfbfsl{S}$', + u'\U0001d64f': '$\\mathsfbfsl{T}$', + u'\U0001d650': '$\\mathsfbfsl{U}$', + u'\U0001d651': '$\\mathsfbfsl{V}$', + u'\U0001d652': '$\\mathsfbfsl{W}$', + u'\U0001d653': '$\\mathsfbfsl{X}$', + u'\U0001d654': '$\\mathsfbfsl{Y}$', + u'\U0001d655': '$\\mathsfbfsl{Z}$', + u'\U0001d656': '$\\mathsfbfsl{a}$', + u'\U0001d657': '$\\mathsfbfsl{b}$', + u'\U0001d658': '$\\mathsfbfsl{c}$', + u'\U0001d659': '$\\mathsfbfsl{d}$', + u'\U0001d65a': '$\\mathsfbfsl{e}$', + u'\U0001d65b': '$\\mathsfbfsl{f}$', + u'\U0001d65c': '$\\mathsfbfsl{g}$', + u'\U0001d65d': '$\\mathsfbfsl{h}$', + u'\U0001d65e': '$\\mathsfbfsl{i}$', + u'\U0001d65f': '$\\mathsfbfsl{j}$', + u'\U0001d660': '$\\mathsfbfsl{k}$', + u'\U0001d661': '$\\mathsfbfsl{l}$', + u'\U0001d662': '$\\mathsfbfsl{m}$', + u'\U0001d663': '$\\mathsfbfsl{n}$', + u'\U0001d664': '$\\mathsfbfsl{o}$', + u'\U0001d665': '$\\mathsfbfsl{p}$', + u'\U0001d666': '$\\mathsfbfsl{q}$', + u'\U0001d667': '$\\mathsfbfsl{r}$', + u'\U0001d668': '$\\mathsfbfsl{s}$', + u'\U0001d669': '$\\mathsfbfsl{t}$', + u'\U0001d66a': '$\\mathsfbfsl{u}$', + u'\U0001d66b': '$\\mathsfbfsl{v}$', + u'\U0001d66c': '$\\mathsfbfsl{w}$', + u'\U0001d66d': '$\\mathsfbfsl{x}$', + u'\U0001d66e': '$\\mathsfbfsl{y}$', + u'\U0001d66f': '$\\mathsfbfsl{z}$', + u'\U0001d670': '$\\mathtt{A}$', + u'\U0001d671': '$\\mathtt{B}$', + u'\U0001d672': '$\\mathtt{C}$', + u'\U0001d673': '$\\mathtt{D}$', + u'\U0001d674': '$\\mathtt{E}$', + u'\U0001d675': '$\\mathtt{F}$', + u'\U0001d676': '$\\mathtt{G}$', + u'\U0001d677': '$\\mathtt{H}$', + u'\U0001d678': '$\\mathtt{I}$', + u'\U0001d679': '$\\mathtt{J}$', + u'\U0001d67a': '$\\mathtt{K}$', + u'\U0001d67b': '$\\mathtt{L}$', + u'\U0001d67c': '$\\mathtt{M}$', + u'\U0001d67d': '$\\mathtt{N}$', + u'\U0001d67e': '$\\mathtt{O}$', + u'\U0001d67f': '$\\mathtt{P}$', + u'\U0001d680': '$\\mathtt{Q}$', + u'\U0001d681': '$\\mathtt{R}$', + u'\U0001d682': '$\\mathtt{S}$', + u'\U0001d683': '$\\mathtt{T}$', + u'\U0001d684': '$\\mathtt{U}$', + u'\U0001d685': '$\\mathtt{V}$', + u'\U0001d686': '$\\mathtt{W}$', + u'\U0001d687': '$\\mathtt{X}$', + u'\U0001d688': '$\\mathtt{Y}$', + u'\U0001d689': '$\\mathtt{Z}$', + u'\U0001d68a': '$\\mathtt{a}$', + u'\U0001d68b': '$\\mathtt{b}$', + u'\U0001d68c': '$\\mathtt{c}$', + u'\U0001d68d': '$\\mathtt{d}$', + u'\U0001d68e': '$\\mathtt{e}$', + u'\U0001d68f': '$\\mathtt{f}$', + u'\U0001d690': '$\\mathtt{g}$', + u'\U0001d691': '$\\mathtt{h}$', + u'\U0001d692': '$\\mathtt{i}$', + u'\U0001d693': '$\\mathtt{j}$', + u'\U0001d694': '$\\mathtt{k}$', + u'\U0001d695': '$\\mathtt{l}$', + u'\U0001d696': '$\\mathtt{m}$', + u'\U0001d697': '$\\mathtt{n}$', + u'\U0001d698': '$\\mathtt{o}$', + u'\U0001d699': '$\\mathtt{p}$', + u'\U0001d69a': '$\\mathtt{q}$', + u'\U0001d69b': '$\\mathtt{r}$', + u'\U0001d69c': '$\\mathtt{s}$', + u'\U0001d69d': '$\\mathtt{t}$', + u'\U0001d69e': '$\\mathtt{u}$', + u'\U0001d69f': '$\\mathtt{v}$', + u'\U0001d6a0': '$\\mathtt{w}$', + u'\U0001d6a1': '$\\mathtt{x}$', + u'\U0001d6a2': '$\\mathtt{y}$', + u'\U0001d6a3': '$\\mathtt{z}$', + u'\U0001d6a8': '$\\mathbf{\\Alpha}$', + u'\U0001d6a9': '$\\mathbf{\\Beta}$', + u'\U0001d6aa': '$\\mathbf{\\Gamma}$', + u'\U0001d6ab': '$\\mathbf{\\Delta}$', + u'\U0001d6ac': '$\\mathbf{\\Epsilon}$', + u'\U0001d6ad': '$\\mathbf{\\Zeta}$', + u'\U0001d6ae': '$\\mathbf{\\Eta}$', + u'\U0001d6af': '$\\mathbf{\\Theta}$', + u'\U0001d6b0': '$\\mathbf{\\Iota}$', + u'\U0001d6b1': '$\\mathbf{\\Kappa}$', + u'\U0001d6b2': '$\\mathbf{\\Lambda}$', + u'\U0001d6b3': '$M$', + u'\U0001d6b4': '$N$', + u'\U0001d6b5': '$\\mathbf{\\Xi}$', + u'\U0001d6b6': '$O$', + u'\U0001d6b7': '$\\mathbf{\\Pi}$', + u'\U0001d6b8': '$\\mathbf{\\Rho}$', + u'\U0001d6b9': '{\\mathbf{\\vartheta}}', + u'\U0001d6ba': '$\\mathbf{\\Sigma}$', + u'\U0001d6bb': '$\\mathbf{\\Tau}$', + u'\U0001d6bc': '$\\mathbf{\\Upsilon}$', + u'\U0001d6bd': '$\\mathbf{\\Phi}$', + u'\U0001d6be': '$\\mathbf{\\Chi}$', + u'\U0001d6bf': '$\\mathbf{\\Psi}$', + u'\U0001d6c0': '$\\mathbf{\\Omega}$', + u'\U0001d6c1': '$\\mathbf{\\nabla}$', + u'\U0001d6c2': '$\\mathbf{\\Alpha}$', + u'\U0001d6c3': '$\\mathbf{\\Beta}$', + u'\U0001d6c4': '$\\mathbf{\\Gamma}$', + u'\U0001d6c5': '$\\mathbf{\\Delta}$', + u'\U0001d6c6': '$\\mathbf{\\Epsilon}$', + u'\U0001d6c7': '$\\mathbf{\\Zeta}$', + u'\U0001d6c8': '$\\mathbf{\\Eta}$', + u'\U0001d6c9': '$\\mathbf{\\theta}$', + u'\U0001d6ca': '$\\mathbf{\\Iota}$', + u'\U0001d6cb': '$\\mathbf{\\Kappa}$', + u'\U0001d6cc': '$\\mathbf{\\Lambda}$', + u'\U0001d6cd': '$M$', + u'\U0001d6ce': '$N$', + u'\U0001d6cf': '$\\mathbf{\\Xi}$', + u'\U0001d6d0': '$O$', + u'\U0001d6d1': '$\\mathbf{\\Pi}$', + u'\U0001d6d2': '$\\mathbf{\\Rho}$', + u'\U0001d6d3': '$\\mathbf{\\varsigma}$', + u'\U0001d6d4': '$\\mathbf{\\Sigma}$', + u'\U0001d6d5': '$\\mathbf{\\Tau}$', + u'\U0001d6d6': '$\\mathbf{\\Upsilon}$', + u'\U0001d6d7': '$\\mathbf{\\Phi}$', + u'\U0001d6d8': '$\\mathbf{\\Chi}$', + u'\U0001d6d9': '$\\mathbf{\\Psi}$', + u'\U0001d6da': '$\\mathbf{\\Omega}$', + u'\U0001d6db': '$\\partial$', + u'\U0001d6dc': '$\\in$', + u'\U0001d6dd': '{\\mathbf{\\vartheta}}', + u'\U0001d6de': '{\\mathbf{\\varkappa}}', + u'\U0001d6df': '{\\mathbf{\\phi}}', + u'\U0001d6e0': '{\\mathbf{\\varrho}}', + u'\U0001d6e1': '{\\mathbf{\\varpi}}', + u'\U0001d6e2': '$\\mathsl{\\Alpha}$', + u'\U0001d6e3': '$\\mathsl{\\Beta}$', + u'\U0001d6e4': '$\\mathsl{\\Gamma}$', + u'\U0001d6e5': '$\\mathsl{\\Delta}$', + u'\U0001d6e6': '$\\mathsl{\\Epsilon}$', + u'\U0001d6e7': '$\\mathsl{\\Zeta}$', + u'\U0001d6e8': '$\\mathsl{\\Eta}$', + u'\U0001d6e9': '$\\mathsl{\\Theta}$', + u'\U0001d6ea': '$\\mathsl{\\Iota}$', + u'\U0001d6eb': '$\\mathsl{\\Kappa}$', + u'\U0001d6ec': '$\\mathsl{\\Lambda}$', + u'\U0001d6ed': '$M$', + u'\U0001d6ee': '$N$', + u'\U0001d6ef': '$\\mathsl{\\Xi}$', + u'\U0001d6f0': '$O$', + u'\U0001d6f1': '$\\mathsl{\\Pi}$', + u'\U0001d6f2': '$\\mathsl{\\Rho}$', + u'\U0001d6f3': '{\\mathsl{\\vartheta}}', + u'\U0001d6f4': '$\\mathsl{\\Sigma}$', + u'\U0001d6f5': '$\\mathsl{\\Tau}$', + u'\U0001d6f6': '$\\mathsl{\\Upsilon}$', + u'\U0001d6f7': '$\\mathsl{\\Phi}$', + u'\U0001d6f8': '$\\mathsl{\\Chi}$', + u'\U0001d6f9': '$\\mathsl{\\Psi}$', + u'\U0001d6fa': '$\\mathsl{\\Omega}$', + u'\U0001d6fb': '$\\mathsl{\\nabla}$', + u'\U0001d6fc': '$\\mathsl{\\Alpha}$', + u'\U0001d6fd': '$\\mathsl{\\Beta}$', + u'\U0001d6fe': '$\\mathsl{\\Gamma}$', + u'\U0001d6ff': '$\\mathsl{\\Delta}$', + u'\U0001d700': '$\\mathsl{\\Epsilon}$', + u'\U0001d701': '$\\mathsl{\\Zeta}$', + u'\U0001d702': '$\\mathsl{\\Eta}$', + u'\U0001d703': '$\\mathsl{\\Theta}$', + u'\U0001d704': '$\\mathsl{\\Iota}$', + u'\U0001d705': '$\\mathsl{\\Kappa}$', + u'\U0001d706': '$\\mathsl{\\Lambda}$', + u'\U0001d707': '$M$', + u'\U0001d708': '$N$', + u'\U0001d709': '$\\mathsl{\\Xi}$', + u'\U0001d70a': '$O$', + u'\U0001d70b': '$\\mathsl{\\Pi}$', + u'\U0001d70c': '$\\mathsl{\\Rho}$', + u'\U0001d70d': '$\\mathsl{\\varsigma}$', + u'\U0001d70e': '$\\mathsl{\\Sigma}$', + u'\U0001d70f': '$\\mathsl{\\Tau}$', + u'\U0001d710': '$\\mathsl{\\Upsilon}$', + u'\U0001d711': '$\\mathsl{\\Phi}$', + u'\U0001d712': '$\\mathsl{\\Chi}$', + u'\U0001d713': '$\\mathsl{\\Psi}$', + u'\U0001d714': '$\\mathsl{\\Omega}$', + u'\U0001d715': '$\\partial$', + u'\U0001d716': '$\\in$', + u'\U0001d717': '{\\mathsl{\\vartheta}}', + u'\U0001d718': '{\\mathsl{\\varkappa}}', + u'\U0001d719': '{\\mathsl{\\phi}}', + u'\U0001d71a': '{\\mathsl{\\varrho}}', + u'\U0001d71b': '{\\mathsl{\\varpi}}', + u'\U0001d71c': '$\\mathbit{\\Alpha}$', + u'\U0001d71d': '$\\mathbit{\\Beta}$', + u'\U0001d71e': '$\\mathbit{\\Gamma}$', + u'\U0001d71f': '$\\mathbit{\\Delta}$', + u'\U0001d720': '$\\mathbit{\\Epsilon}$', + u'\U0001d721': '$\\mathbit{\\Zeta}$', + u'\U0001d722': '$\\mathbit{\\Eta}$', + u'\U0001d723': '$\\mathbit{\\Theta}$', + u'\U0001d724': '$\\mathbit{\\Iota}$', + u'\U0001d725': '$\\mathbit{\\Kappa}$', + u'\U0001d726': '$\\mathbit{\\Lambda}$', + u'\U0001d727': '$M$', + u'\U0001d728': '$N$', + u'\U0001d729': '$\\mathbit{\\Xi}$', + u'\U0001d72a': '$O$', + u'\U0001d72b': '$\\mathbit{\\Pi}$', + u'\U0001d72c': '$\\mathbit{\\Rho}$', + u'\U0001d72d': '{\\mathbit{O}}', + u'\U0001d72e': '$\\mathbit{\\Sigma}$', + u'\U0001d72f': '$\\mathbit{\\Tau}$', + u'\U0001d730': '$\\mathbit{\\Upsilon}$', + u'\U0001d731': '$\\mathbit{\\Phi}$', + u'\U0001d732': '$\\mathbit{\\Chi}$', + u'\U0001d733': '$\\mathbit{\\Psi}$', + u'\U0001d734': '$\\mathbit{\\Omega}$', + u'\U0001d735': '$\\mathbit{\\nabla}$', + u'\U0001d736': '$\\mathbit{\\Alpha}$', + u'\U0001d737': '$\\mathbit{\\Beta}$', + u'\U0001d738': '$\\mathbit{\\Gamma}$', + u'\U0001d739': '$\\mathbit{\\Delta}$', + u'\U0001d73a': '$\\mathbit{\\Epsilon}$', + u'\U0001d73b': '$\\mathbit{\\Zeta}$', + u'\U0001d73c': '$\\mathbit{\\Eta}$', + u'\U0001d73d': '$\\mathbit{\\Theta}$', + u'\U0001d73e': '$\\mathbit{\\Iota}$', + u'\U0001d73f': '$\\mathbit{\\Kappa}$', + u'\U0001d740': '$\\mathbit{\\Lambda}$', + u'\U0001d741': '$M$', + u'\U0001d742': '$N$', + u'\U0001d743': '$\\mathbit{\\Xi}$', + u'\U0001d744': '$O$', + u'\U0001d745': '$\\mathbit{\\Pi}$', + u'\U0001d746': '$\\mathbit{\\Rho}$', + u'\U0001d747': '$\\mathbit{\\varsigma}$', + u'\U0001d748': '$\\mathbit{\\Sigma}$', + u'\U0001d749': '$\\mathbit{\\Tau}$', + u'\U0001d74a': '$\\mathbit{\\Upsilon}$', + u'\U0001d74b': '$\\mathbit{\\Phi}$', + u'\U0001d74c': '$\\mathbit{\\Chi}$', + u'\U0001d74d': '$\\mathbit{\\Psi}$', + u'\U0001d74e': '$\\mathbit{\\Omega}$', + u'\U0001d74f': '$\\partial$', + u'\U0001d750': '$\\in$', + u'\U0001d751': '{\\mathbit{\\vartheta}}', + u'\U0001d752': '{\\mathbit{\\varkappa}}', + u'\U0001d753': '{\\mathbit{\\phi}}', + u'\U0001d754': '{\\mathbit{\\varrho}}', + u'\U0001d755': '{\\mathbit{\\varpi}}', + u'\U0001d756': '$\\mathsfbf{\\Alpha}$', + u'\U0001d757': '$\\mathsfbf{\\Beta}$', + u'\U0001d758': '$\\mathsfbf{\\Gamma}$', + u'\U0001d759': '$\\mathsfbf{\\Delta}$', + u'\U0001d75a': '$\\mathsfbf{\\Epsilon}$', + u'\U0001d75b': '$\\mathsfbf{\\Zeta}$', + u'\U0001d75c': '$\\mathsfbf{\\Eta}$', + u'\U0001d75d': '$\\mathsfbf{\\Theta}$', + u'\U0001d75e': '$\\mathsfbf{\\Iota}$', + u'\U0001d75f': '$\\mathsfbf{\\Kappa}$', + u'\U0001d760': '$\\mathsfbf{\\Lambda}$', + u'\U0001d761': '$M$', + u'\U0001d762': '$N$', + u'\U0001d763': '$\\mathsfbf{\\Xi}$', + u'\U0001d764': '$O$', + u'\U0001d765': '$\\mathsfbf{\\Pi}$', + u'\U0001d766': '$\\mathsfbf{\\Rho}$', + u'\U0001d767': '{\\mathsfbf{\\vartheta}}', + u'\U0001d768': '$\\mathsfbf{\\Sigma}$', + u'\U0001d769': '$\\mathsfbf{\\Tau}$', + u'\U0001d76a': '$\\mathsfbf{\\Upsilon}$', + u'\U0001d76b': '$\\mathsfbf{\\Phi}$', + u'\U0001d76c': '$\\mathsfbf{\\Chi}$', + u'\U0001d76d': '$\\mathsfbf{\\Psi}$', + u'\U0001d76e': '$\\mathsfbf{\\Omega}$', + u'\U0001d76f': '$\\mathsfbf{\\nabla}$', + u'\U0001d770': '$\\mathsfbf{\\Alpha}$', + u'\U0001d771': '$\\mathsfbf{\\Beta}$', + u'\U0001d772': '$\\mathsfbf{\\Gamma}$', + u'\U0001d773': '$\\mathsfbf{\\Delta}$', + u'\U0001d774': '$\\mathsfbf{\\Epsilon}$', + u'\U0001d775': '$\\mathsfbf{\\Zeta}$', + u'\U0001d776': '$\\mathsfbf{\\Eta}$', + u'\U0001d777': '$\\mathsfbf{\\Theta}$', + u'\U0001d778': '$\\mathsfbf{\\Iota}$', + u'\U0001d779': '$\\mathsfbf{\\Kappa}$', + u'\U0001d77a': '$\\mathsfbf{\\Lambda}$', + u'\U0001d77b': '$M$', + u'\U0001d77c': '$N$', + u'\U0001d77d': '$\\mathsfbf{\\Xi}$', + u'\U0001d77e': '$O$', + u'\U0001d77f': '$\\mathsfbf{\\Pi}$', + u'\U0001d780': '$\\mathsfbf{\\Rho}$', + u'\U0001d781': '$\\mathsfbf{\\varsigma}$', + u'\U0001d782': '$\\mathsfbf{\\Sigma}$', + u'\U0001d783': '$\\mathsfbf{\\Tau}$', + u'\U0001d784': '$\\mathsfbf{\\Upsilon}$', + u'\U0001d785': '$\\mathsfbf{\\Phi}$', + u'\U0001d786': '$\\mathsfbf{\\Chi}$', + u'\U0001d787': '$\\mathsfbf{\\Psi}$', + u'\U0001d788': '$\\mathsfbf{\\Omega}$', + u'\U0001d789': '$\\partial$', + u'\U0001d78a': '$\\in$', + u'\U0001d78b': '{\\mathsfbf{\\vartheta}}', + u'\U0001d78c': '{\\mathsfbf{\\varkappa}}', + u'\U0001d78d': '{\\mathsfbf{\\phi}}', + u'\U0001d78e': '{\\mathsfbf{\\varrho}}', + u'\U0001d78f': '{\\mathsfbf{\\varpi}}', + u'\U0001d790': '$\\mathsfbfsl{\\Alpha}$', + u'\U0001d791': '$\\mathsfbfsl{\\Beta}$', + u'\U0001d792': '$\\mathsfbfsl{\\Gamma}$', + u'\U0001d793': '$\\mathsfbfsl{\\Delta}$', + u'\U0001d794': '$\\mathsfbfsl{\\Epsilon}$', + u'\U0001d795': '$\\mathsfbfsl{\\Zeta}$', + u'\U0001d796': '$\\mathsfbfsl{\\Eta}$', + u'\U0001d797': '$\\mathsfbfsl{\\vartheta}$', + u'\U0001d798': '$\\mathsfbfsl{\\Iota}$', + u'\U0001d799': '$\\mathsfbfsl{\\Kappa}$', + u'\U0001d79a': '$\\mathsfbfsl{\\Lambda}$', + u'\U0001d79b': '$M$', + u'\U0001d79c': '$N$', + u'\U0001d79d': '$\\mathsfbfsl{\\Xi}$', + u'\U0001d79e': '$O$', + u'\U0001d79f': '$\\mathsfbfsl{\\Pi}$', + u'\U0001d7a0': '$\\mathsfbfsl{\\Rho}$', + u'\U0001d7a1': '{\\mathsfbfsl{\\vartheta}}', + u'\U0001d7a2': '$\\mathsfbfsl{\\Sigma}$', + u'\U0001d7a3': '$\\mathsfbfsl{\\Tau}$', + u'\U0001d7a4': '$\\mathsfbfsl{\\Upsilon}$', + u'\U0001d7a5': '$\\mathsfbfsl{\\Phi}$', + u'\U0001d7a6': '$\\mathsfbfsl{\\Chi}$', + u'\U0001d7a7': '$\\mathsfbfsl{\\Psi}$', + u'\U0001d7a8': '$\\mathsfbfsl{\\Omega}$', + u'\U0001d7a9': '$\\mathsfbfsl{\\nabla}$', + u'\U0001d7aa': '$\\mathsfbfsl{\\Alpha}$', + u'\U0001d7ab': '$\\mathsfbfsl{\\Beta}$', + u'\U0001d7ac': '$\\mathsfbfsl{\\Gamma}$', + u'\U0001d7ad': '$\\mathsfbfsl{\\Delta}$', + u'\U0001d7ae': '$\\mathsfbfsl{\\Epsilon}$', + u'\U0001d7af': '$\\mathsfbfsl{\\Zeta}$', + u'\U0001d7b0': '$\\mathsfbfsl{\\Eta}$', + u'\U0001d7b1': '$\\mathsfbfsl{\\vartheta}$', + u'\U0001d7b2': '$\\mathsfbfsl{\\Iota}$', + u'\U0001d7b3': '$\\mathsfbfsl{\\Kappa}$', + u'\U0001d7b4': '$\\mathsfbfsl{\\Lambda}$', + u'\U0001d7b5': '$M$', + u'\U0001d7b6': '$N$', + u'\U0001d7b7': '$\\mathsfbfsl{\\Xi}$', + u'\U0001d7b8': '$O$', + u'\U0001d7b9': '$\\mathsfbfsl{\\Pi}$', + u'\U0001d7ba': '$\\mathsfbfsl{\\Rho}$', + u'\U0001d7bb': '$\\mathsfbfsl{\\varsigma}$', + u'\U0001d7bc': '$\\mathsfbfsl{\\Sigma}$', + u'\U0001d7bd': '$\\mathsfbfsl{\\Tau}$', + u'\U0001d7be': '$\\mathsfbfsl{\\Upsilon}$', + u'\U0001d7bf': '$\\mathsfbfsl{\\Phi}$', + u'\U0001d7c0': '$\\mathsfbfsl{\\Chi}$', + u'\U0001d7c1': '$\\mathsfbfsl{\\Psi}$', + u'\U0001d7c2': '$\\mathsfbfsl{\\Omega}$', + u'\U0001d7c3': '$\\partial$', + u'\U0001d7c4': '$\\in$', + u'\U0001d7c5': '{\\mathsfbfsl{\\vartheta}}', + u'\U0001d7c6': '{\\mathsfbfsl{\\varkappa}}', + u'\U0001d7c7': '{\\mathsfbfsl{\\phi}}', + u'\U0001d7c8': '{\\mathsfbfsl{\\varrho}}', + u'\U0001d7c9': '{\\mathsfbfsl{\\varpi}}', + u'\U0001d7ce': '$\\mathbf{0}$', + u'\U0001d7cf': '$\\mathbf{1}$', + u'\U0001d7d0': '$\\mathbf{2}$', + u'\U0001d7d1': '$\\mathbf{3}$', + u'\U0001d7d2': '$\\mathbf{4}$', + u'\U0001d7d3': '$\\mathbf{5}$', + u'\U0001d7d4': '$\\mathbf{6}$', + u'\U0001d7d5': '$\\mathbf{7}$', + u'\U0001d7d6': '$\\mathbf{8}$', + u'\U0001d7d7': '$\\mathbf{9}$', + u'\U0001d7d8': '$\\mathbb{0}$', + u'\U0001d7d9': '$\\mathbb{1}$', + u'\U0001d7da': '$\\mathbb{2}$', + u'\U0001d7db': '$\\mathbb{3}$', + u'\U0001d7dc': '$\\mathbb{4}$', + u'\U0001d7dd': '$\\mathbb{5}$', + u'\U0001d7de': '$\\mathbb{6}$', + u'\U0001d7df': '$\\mathbb{7}$', + u'\U0001d7e0': '$\\mathbb{8}$', + u'\U0001d7e1': '$\\mathbb{9}$', + u'\U0001d7e2': '$\\mathsf{0}$', + u'\U0001d7e3': '$\\mathsf{1}$', + u'\U0001d7e4': '$\\mathsf{2}$', + u'\U0001d7e5': '$\\mathsf{3}$', + u'\U0001d7e6': '$\\mathsf{4}$', + u'\U0001d7e7': '$\\mathsf{5}$', + u'\U0001d7e8': '$\\mathsf{6}$', + u'\U0001d7e9': '$\\mathsf{7}$', + u'\U0001d7ea': '$\\mathsf{8}$', + u'\U0001d7eb': '$\\mathsf{9}$', + u'\U0001d7ec': '$\\mathsfbf{0}$', + u'\U0001d7ed': '$\\mathsfbf{1}$', + u'\U0001d7ee': '$\\mathsfbf{2}$', + u'\U0001d7ef': '$\\mathsfbf{3}$', + u'\U0001d7f0': '$\\mathsfbf{4}$', + u'\U0001d7f1': '$\\mathsfbf{5}$', + u'\U0001d7f2': '$\\mathsfbf{6}$', + u'\U0001d7f3': '$\\mathsfbf{7}$', + u'\U0001d7f4': '$\\mathsfbf{8}$', + u'\U0001d7f5': '$\\mathsfbf{9}$', + u'\U0001d7f6': '$\\mathtt{0}$', + u'\U0001d7f7': '$\\mathtt{1}$', + u'\U0001d7f8': '$\\mathtt{2}$', + u'\U0001d7f9': '$\\mathtt{3}$', + u'\U0001d7fa': '$\\mathtt{4}$', + u'\U0001d7fb': '$\\mathtt{5}$', + u'\U0001d7fc': '$\\mathtt{6}$', + u'\U0001d7fd': '$\\mathtt{7}$', + u'\U0001d7fe': '$\\mathtt{8}$', + u'\U0001d7ff': '$\\mathtt{9}$' + } + +entity_mapping = { + '—':'{---}', + '–':'{--}', + '"':'{"}', + } + +def ValidateCitationKey(text): + """ + removes characters not allowed in BibTeX keys + + >>> from bibliograph.core.utils import _validKey + >>> _validKey(DummyEntry('Foo Bar')) + 'FooBar' + + >>> _validKey(DummyEntry('my@id')) + 'myid' + + """ + # This substitution is based on the description of cite key restrictions at + # http://bibdesk.sourceforge.net/manual/BibDesk%20Help_2.html + return re.sub(u'[ "@\',\\#}{~%&$^]', u'', text) + +def BraceUppercase(text): + """ Convert uppercase letters to bibtex encoded uppercase + + >>> from bibliograph.core.utils import _braceUppercase + >>> _braceUppercase('foo bar') + 'foo bar' + + >>> _braceUppercase('Foo Bar') + '{F}oo {B}ar' + """ + for uc in string.uppercase: + text = text.replace(uc, u'{%s}' % uc) + return text + +def resolveEntities(text): + for entity, entity_map in entity_mapping.iteritems(): + text = text.replace(entity, entity_map) + return text + +def resolveUnicode(text): + #UTF-8 text as entry + for unichar, latexenc in utf8enc2latex_mapping.iteritems() : + text = text.replace(unichar, latexenc) + return text.replace(u'$}{$', u'') + +def escapeSpecialCharacters(text): + """ + latex escaping some (not all) special characters + """ + text.replace('\\', '\\\\') + escape = ['~', '#', '&', '%', '_'] + for c in escape: + text = text.replace(c, '\\' + c ) + return text + +#Calibre functions +#Go from an unicode entry to ASCII Bibtex format without encoding +#Option to go to official ASCII Bibtex or unofficial UTF-8 +def utf8ToBibtex(text, asccii_bibtex = True): + if len(text) == 0: + return '' + text.replace('\\', '\\\\') + text = resolveEntities(text) + if asccii_bibtex : + text = resolveUnicode(text) + return escapeSpecialCharacters(text) + +def bibtex_author_format(item): + #Format authors for Bibtex compliance (get a list as input) + return utf8ToBibtex(u' and'.join([author for author in item]))