mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Bulk editing for device collections in the device view via the context menu
This commit is contained in:
commit
195a3a9cd1
@ -99,7 +99,7 @@ class PRS505(USBMS):
|
||||
if self._card_b_prefix is not None:
|
||||
if not write_cache(self._card_b_prefix):
|
||||
self._card_b_prefix = None
|
||||
|
||||
self.booklist_class.rebuild_collections = self.rebuild_collections
|
||||
|
||||
def get_device_information(self, end_session=True):
|
||||
return (self.gui_name, '', '', '')
|
||||
@ -156,4 +156,10 @@ class PRS505(USBMS):
|
||||
USBMS.sync_booklists(self, booklists, end_session=end_session)
|
||||
debug_print('PRS505: finished sync_booklists')
|
||||
|
||||
def rebuild_collections(self, booklist, oncard):
|
||||
debug_print('PRS505: started rebuild_collections')
|
||||
c = self.initialize_XML_cache()
|
||||
c.rebuild_collections(booklist, {'carda':1, 'cardb':2}.get(oncard, 0))
|
||||
c.write()
|
||||
debug_print('PRS505: finished rebuild_collections')
|
||||
|
||||
|
@ -61,8 +61,7 @@ class XMLCache(object):
|
||||
|
||||
def __init__(self, paths, prefixes, use_author_sort):
|
||||
if DEBUG:
|
||||
debug_print('Building XMLCache...')
|
||||
pprint(paths)
|
||||
debug_print('Building XMLCache...', paths)
|
||||
self.paths = paths
|
||||
self.prefixes = prefixes
|
||||
self.use_author_sort = use_author_sort
|
||||
@ -347,6 +346,12 @@ class XMLCache(object):
|
||||
self.fix_ids()
|
||||
debug_print('Finished update XML from JSON')
|
||||
|
||||
def rebuild_collections(self, booklist, bl_index):
|
||||
if bl_index not in self.record_roots:
|
||||
return
|
||||
root = self.record_roots[bl_index]
|
||||
self.update_playlists(bl_index, root, booklist, [])
|
||||
|
||||
def update_playlists(self, bl_index, root, booklist, collections_attributes):
|
||||
debug_print('Starting update_playlists', collections_attributes)
|
||||
collections = booklist.get_collections(collections_attributes)
|
||||
|
@ -167,3 +167,15 @@ class CollectionsBookList(BookList):
|
||||
books.sort(cmp=lambda x,y:cmp(getter(x), getter(y)))
|
||||
return collections
|
||||
|
||||
def rebuild_collections(self, booklist, oncard):
|
||||
'''
|
||||
For each book in the booklist for the card oncard, remove it from all
|
||||
its current collections, then add it to the collections specified in
|
||||
device_collections.
|
||||
|
||||
oncard is None for the main memory, carda for card A, cardb for card B,
|
||||
etc.
|
||||
|
||||
booklist is the object created by the :method:`books` call above.
|
||||
'''
|
||||
pass
|
||||
|
@ -21,6 +21,7 @@ from calibre.utils.filenames import ascii_filename
|
||||
from calibre.gui2.widgets import IMAGE_EXTENSIONS
|
||||
from calibre.gui2.dialogs.metadata_single import MetadataSingleDialog
|
||||
from calibre.gui2.dialogs.metadata_bulk import MetadataBulkDialog
|
||||
from calibre.gui2.dialogs.tag_list_editor import TagListEditor
|
||||
from calibre.gui2.tools import convert_single_ebook, convert_bulk_ebook, \
|
||||
fetch_scheduled_recipe, generate_catalog
|
||||
from calibre.constants import preferred_encoding, filesystem_encoding, \
|
||||
@ -831,6 +832,21 @@ class EditMetadataAction(object): # {{{
|
||||
db.set_metadata(dest_id, dest_mi, ignore_errors=False)
|
||||
# }}}
|
||||
|
||||
def edit_device_collections(self, view):
|
||||
model = view.model()
|
||||
result = model.get_collections_with_ids()
|
||||
compare = (lambda x,y:cmp(x.lower(), y.lower()))
|
||||
d = TagListEditor(self, tag_to_match=None, data=result, compare=compare)
|
||||
d.exec_()
|
||||
if d.result() == d.Accepted:
|
||||
to_rename = d.to_rename # dict of new text to old id
|
||||
to_delete = d.to_delete # list of ids
|
||||
for text in to_rename:
|
||||
model.rename_collection(old_id=to_rename[text], new_name=unicode(text))
|
||||
for item in to_delete:
|
||||
model.delete_collection_using_id(item)
|
||||
self.upload_collections(model.db, view=view)
|
||||
|
||||
# }}}
|
||||
|
||||
class SaveToDiskAction(object): # {{{
|
||||
|
@ -294,6 +294,11 @@ class DeviceManager(Thread): # {{{
|
||||
return self.create_job(self._sync_booklists, done, args=[booklists],
|
||||
description=_('Send metadata to device'))
|
||||
|
||||
def upload_collections(self, done, booklist, on_card):
|
||||
return self.create_job(booklist.rebuild_collections, done,
|
||||
args=[booklist, on_card],
|
||||
description=_('Send collections to device'))
|
||||
|
||||
def _upload_books(self, files, names, on_card=None, metadata=None):
|
||||
'''Upload books to device: '''
|
||||
return self.device.upload_books(files, names, on_card,
|
||||
@ -1234,6 +1239,18 @@ class DeviceMixin(object): # {{{
|
||||
self.card_a_view.reset()
|
||||
self.card_b_view.reset()
|
||||
|
||||
def _upload_collections(self, job, view):
|
||||
if job.failed:
|
||||
self.device_job_exception(job)
|
||||
view.reset()
|
||||
|
||||
def upload_collections(self, booklist, view):
|
||||
on_card = 'carda' if self.stack.currentIndex() == 2 else \
|
||||
'cardb' if self.stack.currentIndex() == 3 else \
|
||||
None
|
||||
done = partial(self._upload_collections, view=view)
|
||||
return self.device_manager.upload_collections(done, booklist, on_card)
|
||||
|
||||
def upload_books(self, files, names, metadata, on_card=None, memory=None):
|
||||
'''
|
||||
Upload books to device.
|
||||
|
@ -1,54 +1,34 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
from functools import partial
|
||||
from PyQt4.QtCore import SIGNAL, Qt
|
||||
from PyQt4.QtGui import QDialog, QListWidgetItem
|
||||
|
||||
from calibre.gui2.dialogs.tag_list_editor_ui import Ui_TagListEditor
|
||||
from calibre.gui2 import question_dialog, error_dialog
|
||||
from calibre.ebooks.metadata import title_sort
|
||||
|
||||
class TagListEditor(QDialog, Ui_TagListEditor):
|
||||
|
||||
def __init__(self, window, db, tag_to_match, category):
|
||||
def __init__(self, window, tag_to_match, data, compare):
|
||||
QDialog.__init__(self, window)
|
||||
Ui_TagListEditor.__init__(self)
|
||||
self.setupUi(self)
|
||||
|
||||
self.to_rename = {}
|
||||
self.to_delete = []
|
||||
self.db = db
|
||||
self.all_tags = {}
|
||||
self.category = category
|
||||
if category == 'tags':
|
||||
result = db.get_tags_with_ids()
|
||||
compare = (lambda x,y:cmp(x.lower(), y.lower()))
|
||||
elif category == 'series':
|
||||
result = db.get_series_with_ids()
|
||||
compare = (lambda x,y:cmp(title_sort(x).lower(), title_sort(y).lower()))
|
||||
elif category == 'publisher':
|
||||
result = db.get_publishers_with_ids()
|
||||
compare = (lambda x,y:cmp(x.lower(), y.lower()))
|
||||
else: # should be a custom field
|
||||
self.cc_label = None
|
||||
if category in db.field_metadata:
|
||||
self.cc_label = db.field_metadata[category]['label']
|
||||
result = self.db.get_custom_items_with_ids(label=self.cc_label)
|
||||
else:
|
||||
result = []
|
||||
compare = (lambda x,y:cmp(x.lower(), y.lower()))
|
||||
|
||||
for k,v in result:
|
||||
for k,v in data:
|
||||
self.all_tags[v] = k
|
||||
for tag in sorted(self.all_tags.keys(), cmp=compare):
|
||||
item = QListWidgetItem(tag)
|
||||
item.setData(Qt.UserRole, self.all_tags[tag])
|
||||
self.available_tags.addItem(item)
|
||||
|
||||
items = self.available_tags.findItems(tag_to_match, Qt.MatchExactly)
|
||||
if len(items) == 1:
|
||||
self.available_tags.setCurrentItem(items[0])
|
||||
if tag_to_match is not None:
|
||||
items = self.available_tags.findItems(tag_to_match, Qt.MatchExactly)
|
||||
if len(items) == 1:
|
||||
self.available_tags.setCurrentItem(items[0])
|
||||
|
||||
self.connect(self.delete_button, SIGNAL('clicked()'), self.delete_tags)
|
||||
self.connect(self.rename_button, SIGNAL('clicked()'), self.rename_tag)
|
||||
@ -62,11 +42,6 @@ class TagListEditor(QDialog, Ui_TagListEditor):
|
||||
item.setText(self.item_before_editing.text())
|
||||
return
|
||||
if item.text() != self.item_before_editing.text():
|
||||
if item.text() in self.all_tags.keys() or item.text() in self.to_rename.keys():
|
||||
error_dialog(self, _('Item already used'),
|
||||
_('The item %s is already used.')%(item.text())).exec_()
|
||||
item.setText(self.item_before_editing.text())
|
||||
return
|
||||
(id,ign) = self.item_before_editing.data(Qt.UserRole).toInt()
|
||||
self.to_rename[item.text()] = id
|
||||
|
||||
@ -99,30 +74,3 @@ class TagListEditor(QDialog, Ui_TagListEditor):
|
||||
self.to_delete.append(id)
|
||||
self.available_tags.takeItem(self.available_tags.row(item))
|
||||
|
||||
def accept(self):
|
||||
rename_func = None
|
||||
if self.category == 'tags':
|
||||
rename_func = self.db.rename_tag
|
||||
delete_func = self.db.delete_tag_using_id
|
||||
elif self.category == 'series':
|
||||
rename_func = self.db.rename_series
|
||||
delete_func = self.db.delete_series_using_id
|
||||
elif self.category == 'publisher':
|
||||
rename_func = self.db.rename_publisher
|
||||
delete_func = self.db.delete_publisher_using_id
|
||||
else:
|
||||
rename_func = partial(self.db.rename_custom_item, label=self.cc_label)
|
||||
delete_func = partial(self.db.delete_custom_item_using_id, label=self.cc_label)
|
||||
|
||||
work_done = False
|
||||
if rename_func:
|
||||
for text in self.to_rename:
|
||||
work_done = True
|
||||
rename_func(id=self.to_rename[text], new_name=unicode(text))
|
||||
for item in self.to_delete:
|
||||
work_done = True
|
||||
delete_func(item)
|
||||
if not work_done:
|
||||
QDialog.reject(self)
|
||||
else:
|
||||
QDialog.accept(self)
|
||||
|
@ -226,17 +226,22 @@ class LibraryViewMixin(object): # {{{
|
||||
self.action_show_book_details,
|
||||
self.action_del,
|
||||
add_to_library = None,
|
||||
edit_device_collections=None,
|
||||
similar_menu=similar_menu)
|
||||
add_to_library = (_('Add books to library'), self.add_books_from_device)
|
||||
edit_device_collections = (_('Manage collections'), self.edit_device_collections)
|
||||
self.memory_view.set_context_menu(None, None, None,
|
||||
self.action_view, self.action_save, None, None, self.action_del,
|
||||
add_to_library=add_to_library)
|
||||
add_to_library=add_to_library,
|
||||
edit_device_collections=edit_device_collections)
|
||||
self.card_a_view.set_context_menu(None, None, None,
|
||||
self.action_view, self.action_save, None, None, self.action_del,
|
||||
add_to_library=add_to_library)
|
||||
add_to_library=add_to_library,
|
||||
edit_device_collections=edit_device_collections)
|
||||
self.card_b_view.set_context_menu(None, None, None,
|
||||
self.action_view, self.action_save, None, None, self.action_del,
|
||||
add_to_library=add_to_library)
|
||||
add_to_library=add_to_library,
|
||||
edit_device_collections=edit_device_collections)
|
||||
|
||||
self.library_view.files_dropped.connect(self.files_dropped, type=Qt.QueuedConnection)
|
||||
for func, args in [
|
||||
@ -249,8 +254,11 @@ class LibraryViewMixin(object): # {{{
|
||||
getattr(view, func)(*args)
|
||||
|
||||
self.memory_view.connect_dirtied_signal(self.upload_booklists)
|
||||
self.memory_view.connect_upload_collections_signal(self.upload_collections)
|
||||
self.card_a_view.connect_dirtied_signal(self.upload_booklists)
|
||||
self.card_a_view.connect_upload_collections_signal(self.upload_collections)
|
||||
self.card_b_view.connect_dirtied_signal(self.upload_booklists)
|
||||
self.card_b_view.connect_upload_collections_signal(self.upload_collections)
|
||||
|
||||
self.book_on_device(None, reset=True)
|
||||
db.set_book_on_device_func(self.book_on_device)
|
||||
|
@ -857,6 +857,7 @@ class OnDeviceSearch(SearchQueryParser): # {{{
|
||||
class DeviceBooksModel(BooksModel): # {{{
|
||||
|
||||
booklist_dirtied = pyqtSignal()
|
||||
upload_collections = pyqtSignal(object)
|
||||
|
||||
def __init__(self, parent):
|
||||
BooksModel.__init__(self, parent)
|
||||
@ -977,8 +978,8 @@ class DeviceBooksModel(BooksModel): # {{{
|
||||
x, y = int(self.db[x].size), int(self.db[y].size)
|
||||
return cmp(x, y)
|
||||
def tagscmp(x, y):
|
||||
x = ','.join(self.db[x].device_collections)
|
||||
y = ','.join(self.db[y].device_collections)
|
||||
x = ','.join(getattr(self.db[x], 'device_collections', [])).lower()
|
||||
y = ','.join(getattr(self.db[y], 'device_collections', [])).lower()
|
||||
return cmp(x, y)
|
||||
def libcmp(x, y):
|
||||
x, y = self.db[x].in_library, self.db[y].in_library
|
||||
@ -1026,6 +1027,9 @@ class DeviceBooksModel(BooksModel): # {{{
|
||||
def set_database(self, db):
|
||||
self.custom_columns = {}
|
||||
self.db = db
|
||||
for book in db:
|
||||
if book.device_collections is not None:
|
||||
book.device_collections.sort(cmp=lambda x,y: cmp(x.lower(), y.lower()))
|
||||
self.map = list(range(0, len(db)))
|
||||
|
||||
def current_changed(self, current, previous):
|
||||
@ -1079,6 +1083,36 @@ class DeviceBooksModel(BooksModel): # {{{
|
||||
res.append((r,b))
|
||||
return res
|
||||
|
||||
def get_collections_with_ids(self):
|
||||
collections = set()
|
||||
for book in self.db:
|
||||
if book.device_collections is not None:
|
||||
collections.update(set(book.device_collections))
|
||||
self.collections = []
|
||||
result = []
|
||||
for i,collection in enumerate(collections):
|
||||
result.append((i, collection))
|
||||
self.collections.append(collection)
|
||||
return result
|
||||
|
||||
def rename_collection(self, old_id, new_name):
|
||||
old_name = self.collections[old_id]
|
||||
for book in self.db:
|
||||
if book.device_collections is None:
|
||||
continue
|
||||
if old_name in book.device_collections:
|
||||
book.device_collections.remove(old_name)
|
||||
if new_name not in book.device_collections:
|
||||
book.device_collections.append(new_name)
|
||||
|
||||
def delete_collection_using_id(self, old_id):
|
||||
old_name = self.collections[old_id]
|
||||
for book in self.db:
|
||||
if book.device_collections is None:
|
||||
continue
|
||||
if old_name in book.device_collections:
|
||||
book.device_collections.remove(old_name)
|
||||
|
||||
def indices(self, rows):
|
||||
'''
|
||||
Return indices into underlying database from rows
|
||||
@ -1109,7 +1143,7 @@ class DeviceBooksModel(BooksModel): # {{{
|
||||
elif cname == 'collections':
|
||||
tags = self.db[self.map[row]].device_collections
|
||||
if tags:
|
||||
return QVariant(', '.join(sorted(tags, key=str.lower)))
|
||||
return QVariant(', '.join(tags))
|
||||
elif role == Qt.ToolTipRole and index.isValid():
|
||||
if self.map[row] in self.indices_to_be_deleted():
|
||||
return QVariant(_('Marked for deletion'))
|
||||
@ -1151,14 +1185,17 @@ class DeviceBooksModel(BooksModel): # {{{
|
||||
return False
|
||||
val = unicode(value.toString()).strip()
|
||||
idx = self.map[row]
|
||||
if cname == 'collections':
|
||||
tags = [i.strip() for i in val.split(',')]
|
||||
tags = [t for t in tags if t]
|
||||
self.db[idx].device_collections = tags
|
||||
self.upload_collections.emit(self.db)
|
||||
return True
|
||||
|
||||
if cname == 'title' :
|
||||
self.db[idx].title = val
|
||||
elif cname == 'authors':
|
||||
self.db[idx].authors = string_to_authors(val)
|
||||
elif cname == 'collections':
|
||||
tags = [i.strip() for i in val.split(',')]
|
||||
tags = [t for t in tags if t]
|
||||
self.db[idx].device_collections = tags
|
||||
self.dataChanged.emit(index, index)
|
||||
self.booklist_dirtied.emit()
|
||||
done = True
|
||||
|
@ -371,7 +371,8 @@ class BooksView(QTableView): # {{{
|
||||
# Context Menu {{{
|
||||
def set_context_menu(self, edit_metadata, send_to_device, convert, view,
|
||||
save, open_folder, book_details, delete,
|
||||
similar_menu=None, add_to_library=None):
|
||||
similar_menu=None, add_to_library=None,
|
||||
edit_device_collections=None):
|
||||
self.setContextMenuPolicy(Qt.DefaultContextMenu)
|
||||
self.context_menu = QMenu(self)
|
||||
if edit_metadata is not None:
|
||||
@ -393,6 +394,9 @@ class BooksView(QTableView): # {{{
|
||||
if add_to_library is not None:
|
||||
func = partial(add_to_library[1], view=self)
|
||||
self.context_menu.addAction(add_to_library[0], func)
|
||||
if edit_device_collections is not None:
|
||||
func = partial(edit_device_collections[1], view=self)
|
||||
self.context_menu.addAction(edit_device_collections[0], func)
|
||||
|
||||
def contextMenuEvent(self, event):
|
||||
self.context_menu.popup(event.globalPos())
|
||||
@ -505,6 +509,9 @@ class DeviceBooksView(BooksView): # {{{
|
||||
def connect_dirtied_signal(self, slot):
|
||||
self._model.booklist_dirtied.connect(slot)
|
||||
|
||||
def connect_upload_collections_signal(self, func):
|
||||
self._model.upload_collections.connect(partial(func, view=self))
|
||||
|
||||
def dropEvent(self, *args):
|
||||
error_dialog(self, _('Not allowed'),
|
||||
_('Dropping onto a device is not supported. First add the book to the calibre library.')).exec_()
|
||||
|
@ -15,6 +15,7 @@ from PyQt4.Qt import Qt, QTreeView, QApplication, pyqtSignal, \
|
||||
QAbstractItemModel, QVariant, QModelIndex, QMenu, \
|
||||
QPushButton, QWidget, QItemDelegate
|
||||
|
||||
from calibre.ebooks.metadata import title_sort
|
||||
from calibre.gui2 import config, NONE
|
||||
from calibre.utils.config import prefs
|
||||
from calibre.library.field_metadata import TagsIcons
|
||||
@ -680,9 +681,49 @@ class TagBrowserMixin(object): # {{{
|
||||
self.tags_view.recount()
|
||||
|
||||
def do_tags_list_edit(self, tag, category):
|
||||
d = TagListEditor(self, self.library_view.model().db, tag, category)
|
||||
db=self.library_view.model().db
|
||||
if category == 'tags':
|
||||
result = db.get_tags_with_ids()
|
||||
compare = (lambda x,y:cmp(x.lower(), y.lower()))
|
||||
elif category == 'series':
|
||||
result = db.get_series_with_ids()
|
||||
compare = (lambda x,y:cmp(title_sort(x).lower(), title_sort(y).lower()))
|
||||
elif category == 'publisher':
|
||||
result = db.get_publishers_with_ids()
|
||||
compare = (lambda x,y:cmp(x.lower(), y.lower()))
|
||||
else: # should be a custom field
|
||||
cc_label = None
|
||||
if category in db.field_metadata:
|
||||
cc_label = db.field_metadata[category]['label']
|
||||
result = self.db.get_custom_items_with_ids(label=cc_label)
|
||||
else:
|
||||
result = []
|
||||
compare = (lambda x,y:cmp(x.lower(), y.lower()))
|
||||
|
||||
d = TagListEditor(self, tag_to_match=tag, data=result, compare=compare)
|
||||
d.exec_()
|
||||
if d.result() == d.Accepted:
|
||||
to_rename = d.to_rename # dict of new text to old id
|
||||
to_delete = d.to_delete # list of ids
|
||||
rename_func = None
|
||||
if category == 'tags':
|
||||
rename_func = db.rename_tag
|
||||
delete_func = db.delete_tag_using_id
|
||||
elif category == 'series':
|
||||
rename_func = db.rename_series
|
||||
delete_func = db.delete_series_using_id
|
||||
elif category == 'publisher':
|
||||
rename_func = db.rename_publisher
|
||||
delete_func = db.delete_publisher_using_id
|
||||
else:
|
||||
rename_func = partial(db.rename_custom_item, label=cc_label)
|
||||
delete_func = partial(db.delete_custom_item_using_id, label=cc_label)
|
||||
if rename_func:
|
||||
for text in to_rename:
|
||||
rename_func(old_id=to_rename[text], new_name=unicode(text))
|
||||
for item in to_delete:
|
||||
delete_func(item)
|
||||
|
||||
# Clean up everything, as information could have changed for many books.
|
||||
self.library_view.model().refresh()
|
||||
self.tags_view.set_new_model()
|
||||
|
@ -104,14 +104,20 @@ will appear in the next release of |app|.
|
||||
How does |app| manage collections on my SONY reader?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When you send a book to the device, |app| will create collections based on the metadata for that book. By default, collections are created
|
||||
from tags and series. You can control what metadata is used by going to Preferences->Plugins->Device Interface plugins and customizing
|
||||
When |app| connects with the device, it retrieves all collections for the books on the device. The collections
|
||||
of which books are members are shown on the device view.
|
||||
|
||||
When you send a book to the device, |app| will if necessary create new collections based on the metadata for
|
||||
that book, then add the book to the collections. By default, collections are created from tags and series. You
|
||||
can control what metadata is used by going to Preferences->Plugins->Device Interface plugins and customizing
|
||||
the SONY device interface plugin.
|
||||
|
||||
You can edit collections on the device in the device view in |app| by double clicking or right clicking on the collections field.
|
||||
|app| will not delete already existing collections for a book on your device when you resend the book to the
|
||||
device. To ensure that the collections are based only on current |app| metadata, first delete the books from
|
||||
the device, and then resend the books.
|
||||
|
||||
|app| will not delete already existing collections on your device. To ensure that the collections are based only on current |app| metadata,
|
||||
delete and resend the books to the device.
|
||||
You can edit collections on the device in the device view in |app| by double clicking or right clicking on the
|
||||
collections field. This is the only way to remove a book from a collection.
|
||||
|
||||
Can I use both |app| and the SONY software to manage my reader?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -296,7 +302,7 @@ Take your pick:
|
||||
|
||||
Why does |app| show only some of my fonts on OS X?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|app| embeds fonts in ebook files it creates. E-book files support embedding only TrueType (.ttf) fonts. Most fonts on OS X systems are in .dfont format, thus they cannot be embedded. |app| shows only TrueType fonts founf on your system. You can obtain many TrueType fonts on the web. Simply download the .ttf files and add them to the Library/Fonts directory in your home directory.
|
||||
|app| embeds fonts in ebook files it creates. E-book files support embedding only TrueType (.ttf) fonts. Most fonts on OS X systems are in .dfont format, thus they cannot be embedded. |app| shows only TrueType fonts found on your system. You can obtain many TrueType fonts on the web. Simply download the .ttf files and add them to the Library/Fonts directory in your home directory.
|
||||
|
||||
|app| is not starting on Windows?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
Loading…
x
Reference in New Issue
Block a user