mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 18:54:09 -04:00
Notes editor: Allow specifying image sizes
This commit is contained in:
parent
3d7d07ed78
commit
c1ca6616bd
@ -4,6 +4,7 @@
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import weakref
|
||||
from collections import defaultdict
|
||||
from contextlib import contextmanager
|
||||
@ -14,13 +15,13 @@ from qt.core import (
|
||||
QAction, QApplication, QBrush, QByteArray, QCheckBox, QColor, QColorDialog, QDialog,
|
||||
QDialogButtonBox, QFont, QFontInfo, QFontMetrics, QFormLayout, QHBoxLayout, QIcon,
|
||||
QKeySequence, QLabel, QLineEdit, QMenu, QPalette, QPlainTextEdit, QPointF,
|
||||
QPushButton, QSize, QSyntaxHighlighter, Qt, QTabWidget, QTextBlockFormat,
|
||||
QPushButton, QSize, QSpinBox, QSyntaxHighlighter, Qt, QTabWidget, QTextBlockFormat,
|
||||
QTextCharFormat, QTextCursor, QTextDocument, QTextEdit, QTextFormat,
|
||||
QTextFrameFormat, QTextListFormat, QTimer, QToolButton, QUrl, QVBoxLayout, QWidget,
|
||||
pyqtSignal, pyqtSlot,
|
||||
QTextFrameFormat, QTextImageFormat, QTextListFormat, QTimer, QToolButton, QUrl,
|
||||
QVBoxLayout, QWidget, pyqtSignal, pyqtSlot,
|
||||
)
|
||||
|
||||
from calibre import xml_replace_entities
|
||||
from calibre import fit_image, xml_replace_entities
|
||||
from calibre.db.constants import DATA_DIR_NAME
|
||||
from calibre.ebooks.chardet import xml_to_unicode
|
||||
from calibre.gui2 import (
|
||||
@ -914,6 +915,56 @@ class EditorWidget(QTextEdit, LineEditECM): # {{{
|
||||
if fmt.isImageFormat():
|
||||
c.deletePreviousChar()
|
||||
|
||||
def resize_image_at(self, cursor_pos):
|
||||
c = self.textCursor()
|
||||
c.clearSelection()
|
||||
c.setPosition(cursor_pos)
|
||||
c.movePosition(QTextCursor.MoveOperation.PreviousCharacter, QTextCursor.MoveMode.KeepAnchor)
|
||||
fmt = c.charFormat()
|
||||
if not fmt.isImageFormat():
|
||||
return
|
||||
fmt = fmt.toImageFormat()
|
||||
from calibre.utils.img import image_from_data
|
||||
img = image_from_data(self.loadResource(QTextDocument.ResourceType.ImageResource, QUrl(fmt.name())))
|
||||
w, h = int(fmt.width()), int(fmt.height())
|
||||
d = QDialog(self)
|
||||
l = QVBoxLayout(d)
|
||||
la = QLabel(_('Shrink image to fit within:'))
|
||||
h = QHBoxLayout()
|
||||
l.addLayout(h)
|
||||
la = QLabel(_('&Width:'))
|
||||
h.addWidget(la)
|
||||
d.width = w = QSpinBox(self)
|
||||
w.setRange(0, 10000), w.setSuffix(' px')
|
||||
w.setValue(int(fmt.width()))
|
||||
h.addWidget(w), la.setBuddy(w)
|
||||
w.setSpecialValueText(' ')
|
||||
la = QLabel(_('&Height:'))
|
||||
h.addWidget(la)
|
||||
d.height = w = QSpinBox(self)
|
||||
w.setRange(0, 10000), w.setSuffix(' px')
|
||||
w.setValue(int(fmt.height()))
|
||||
h.addWidget(w), la.setBuddy(w)
|
||||
w.setSpecialValueText(' ')
|
||||
h.addStretch(10)
|
||||
bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
|
||||
bb.accepted.connect(d.accept), bb.rejected.connect(d.reject)
|
||||
d.setWindowTitle(_('Enter new size for image'))
|
||||
l.addWidget(bb)
|
||||
d.resize(d.sizeHint())
|
||||
|
||||
if d.exec() == QDialog.DialogCode.Accepted:
|
||||
page_width, page_height = (d.width.value() or sys.maxsize), (d.height.value() or sys.maxsize)
|
||||
w, h = int(img.width()), int(img.height())
|
||||
resized, nw, nh = fit_image(w, h, page_width, page_height)
|
||||
if resized:
|
||||
fmt.setWidth(nw), fmt.setHeight(nh)
|
||||
else:
|
||||
f = QTextImageFormat()
|
||||
f.setName(fmt.name())
|
||||
fmt = f
|
||||
c.setCharFormat(fmt)
|
||||
|
||||
def align_image_at(self, cursor_pos, alignment):
|
||||
c = self.textCursor()
|
||||
c.clearSelection()
|
||||
@ -971,6 +1022,9 @@ class EditorWidget(QTextEdit, LineEditECM): # {{{
|
||||
ac.triggered.connect(partial(self.align_image_at, c.position(), epos))
|
||||
if pos == epos:
|
||||
ac.setChecked(True)
|
||||
cs = align_menu.addAction(QIcon.ic('resize.png'), _('Change size'))
|
||||
cs.triggered.connect(partial(self.resize_image_at, c.position()))
|
||||
align_menu.addSeparator()
|
||||
a(_('Float to the left'), QTextFrameFormat.Position.FloatLeft)
|
||||
a(_('Inline with text'), QTextFrameFormat.Position.InFlow)
|
||||
a(_('Float to the right'), QTextFrameFormat.Position.FloatRight)
|
||||
|
@ -2,15 +2,16 @@
|
||||
# License: GPLv3 Copyright: 2023, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import os
|
||||
import sys
|
||||
from qt.core import (
|
||||
QButtonGroup, QByteArray, QDialog, QDialogButtonBox, QFormLayout, QHBoxLayout,
|
||||
QIcon, QLabel, QLineEdit, QPixmap, QPushButton, QRadioButton, QSize, Qt,
|
||||
QIcon, QLabel, QLineEdit, QPixmap, QPushButton, QRadioButton, QSize, QSpinBox, Qt,
|
||||
QTextDocument, QTextFrameFormat, QTextImageFormat, QUrl, QVBoxLayout, QWidget,
|
||||
pyqtSlot,
|
||||
)
|
||||
from typing import NamedTuple
|
||||
|
||||
from calibre import sanitize_file_name
|
||||
from calibre import sanitize_file_name, fit_image
|
||||
from calibre.db.constants import RESOURCE_URL_SCHEME
|
||||
from calibre.db.notes.connect import hash_data
|
||||
from calibre.db.notes.exim import export_note, import_note
|
||||
@ -83,6 +84,7 @@ class AskImage(Dialog):
|
||||
self.v = v = QVBoxLayout(self)
|
||||
self.h = h = QHBoxLayout()
|
||||
v.addLayout(h)
|
||||
|
||||
v.addWidget(self.bb)
|
||||
|
||||
self.image_preview = ip = ImageView(self, 'insert-image-for-notes-preview', True)
|
||||
@ -100,6 +102,15 @@ class AskImage(Dialog):
|
||||
ne.setPlaceholderText(_('Filename for the image'))
|
||||
vr.addWidget(ne)
|
||||
|
||||
self.hb = hb = QHBoxLayout()
|
||||
vr.addLayout(hb)
|
||||
self.add_file_button = b = QPushButton(QIcon.ic('document_open.png'), _('Choose image &file'), self)
|
||||
b.clicked.connect(self.add_file)
|
||||
hb.addWidget(b)
|
||||
self.paste_button = b = QPushButton(QIcon.ic('edit-paste.png'), _('&Paste from clipboard'), self)
|
||||
b.clicked.connect(self.paste_image)
|
||||
hb.addWidget(b)
|
||||
|
||||
self.la2 = la = QLabel(_('Place image:'))
|
||||
vr.addWidget(la)
|
||||
self.hr = hr = QHBoxLayout()
|
||||
@ -113,14 +124,23 @@ class AskImage(Dialog):
|
||||
bg.addButton(r), hr.addWidget(r)
|
||||
self.inline.setChecked(True)
|
||||
|
||||
self.hb = hb = QHBoxLayout()
|
||||
vr.addLayout(hb)
|
||||
self.add_file_button = b = QPushButton(QIcon.ic('document_open.png'), _('Choose image &file'), self)
|
||||
b.clicked.connect(self.add_file)
|
||||
hb.addWidget(b)
|
||||
self.paste_button = b = QPushButton(QIcon.ic('edit-paste.png'), _('&Paste from clipboard'), self)
|
||||
b.clicked.connect(self.paste_image)
|
||||
hb.addWidget(b)
|
||||
self.la2 = la = QLabel(_('Shrink image to fit within:'))
|
||||
vr.addWidget(la)
|
||||
self.hr2 = h = QHBoxLayout()
|
||||
vr.addLayout(h)
|
||||
la = QLabel(_('&Width:'))
|
||||
h.addWidget(la)
|
||||
self.width = w = QSpinBox(self)
|
||||
w.setRange(0, 10000), w.setSuffix(' px')
|
||||
h.addWidget(w), la.setBuddy(w)
|
||||
w.setSpecialValueText(' ')
|
||||
la = QLabel(_('&Height:'))
|
||||
h.addWidget(la)
|
||||
self.height = w = QSpinBox(self)
|
||||
w.setRange(0, 10000), w.setSuffix(' px')
|
||||
h.addWidget(w), la.setBuddy(w)
|
||||
w.setSpecialValueText(' ')
|
||||
h.addStretch(10)
|
||||
|
||||
vr.addStretch(10)
|
||||
self.add_file_button.setFocus(Qt.FocusReason.OtherFocusReason)
|
||||
@ -154,7 +174,7 @@ class AskImage(Dialog):
|
||||
def paste_image(self):
|
||||
if not self.image_preview.paste_from_clipboard():
|
||||
return error_dialog(self, _('Could not paste'), _(
|
||||
'No image is present int he system clipboard'), show=True)
|
||||
'No image is present in the system clipboard'), show=True)
|
||||
|
||||
@property
|
||||
def image_layout(self) -> 'QTextFrameFormat.Position':
|
||||
@ -164,6 +184,15 @@ class AskImage(Dialog):
|
||||
if b is self.float_left:
|
||||
return QTextFrameFormat.Position.FloatLeft
|
||||
return QTextFrameFormat.Position.FloatRight
|
||||
|
||||
@property
|
||||
def image_size(self) -> tuple[int, int]:
|
||||
s = self.image_preview.pixmap().size()
|
||||
return s.width(), s.height()
|
||||
|
||||
@property
|
||||
def bounding_size(self) -> tuple[int, int]:
|
||||
return (self.width.value() or sys.maxsize), (self.height.value() or sys.maxsize)
|
||||
# }}}
|
||||
|
||||
|
||||
@ -238,6 +267,12 @@ class NoteEditorWidget(EditorWidget):
|
||||
fmt = QTextImageFormat()
|
||||
alg, digest = ir.digest.split(':', 1)
|
||||
fmt.setName(RESOURCE_URL_SCHEME + f'://{alg}/{digest}?placement={uuid4()}')
|
||||
page_width, page_height = d.bounding_size
|
||||
w, h = d.image_size
|
||||
resized, nw, nh = fit_image(w, h, page_width, page_height)
|
||||
if resized:
|
||||
fmt.setWidth(nw)
|
||||
fmt.setHeight(nh)
|
||||
c.insertImage(fmt, d.image_layout)
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user