File indexing completed on 2024-09-15 09:10:24
0001 /* 0002 SPDX-License-Identifier: GPL-2.0-or-later 0003 SPDX-FileCopyrightText: 2012 martin Kuettler <martin.kuettler@gmail.com> 0004 */ 0005 0006 #include "imageentry.h" 0007 #include "actionbar.h" 0008 #include "worksheetimageitem.h" 0009 #include "worksheetview.h" 0010 #include "lib/jupyterutils.h" 0011 0012 #include <QDir> 0013 #include <QMenu> 0014 #include <QFileSystemWatcher> 0015 #include <QJsonValue> 0016 #include <QJsonObject> 0017 #include <QStandardPaths> 0018 0019 #include <KLocalizedString> 0020 #include <KZip> 0021 0022 ImageEntry::ImageEntry(Worksheet* worksheet) : WorksheetEntry(worksheet) 0023 { 0024 m_imageItem = nullptr; 0025 m_textItem = new WorksheetTextItem(this); 0026 m_imageWatcher = new QFileSystemWatcher(this); 0027 m_displaySize.width = -1; 0028 m_displaySize.height = -1; 0029 m_printSize.width = -1; 0030 m_printSize.height = -1; 0031 m_displaySize.widthUnit = ImageSize::Auto; 0032 m_displaySize.heightUnit = ImageSize::Auto; 0033 m_printSize.widthUnit = ImageSize::Auto; 0034 m_printSize.heightUnit = ImageSize::Auto; 0035 m_useDisplaySizeForPrinting = true; 0036 connect(m_imageWatcher, &QFileSystemWatcher::fileChanged, this, &ImageEntry::updateEntry); 0037 0038 setFlag(QGraphicsItem::ItemIsFocusable); 0039 updateEntry(); 0040 } 0041 0042 void ImageEntry::populateMenu(QMenu* menu, QPointF pos) 0043 { 0044 WorksheetEntry::populateMenu(menu, pos); 0045 auto* firstAction = menu->actions().at(0); 0046 0047 auto* action = new QAction(QIcon::fromTheme(QLatin1String("configure")), i18n("Configure Image")); 0048 menu->insertAction(firstAction, action); 0049 connect(action, &QAction::triggered, this, &ImageEntry::startConfigDialog); 0050 menu->insertSeparator(firstAction); 0051 } 0052 0053 bool ImageEntry::isEmpty() 0054 { 0055 return false; 0056 } 0057 0058 int ImageEntry::type() const 0059 { 0060 return Type; 0061 } 0062 0063 bool ImageEntry::acceptRichText() 0064 { 0065 return false; 0066 } 0067 0068 void ImageEntry::setContent(const QString& content) 0069 { 0070 Q_UNUSED(content); 0071 return; 0072 } 0073 0074 void ImageEntry::setContent(const QDomElement& content, const KZip& file) 0075 { 0076 Q_UNUSED(file); 0077 0078 QDomElement fileName = content.firstChildElement(QLatin1String("FileName")); 0079 if (!fileName.isNull()) { 0080 m_fileName = fileName.text(); 0081 0082 const KArchiveEntry* imageEntry = file.directory()->entry(m_fileName); 0083 if (imageEntry && imageEntry->isFile()) 0084 { 0085 const KArchiveFile* imageFile = static_cast<const KArchiveFile*>(imageEntry); 0086 const QString& dir = QStandardPaths::writableLocation(QStandardPaths::TempLocation); 0087 imageFile->copyTo(dir); 0088 } 0089 } 0090 0091 static QStringList unitNames; 0092 if (unitNames.isEmpty()) 0093 unitNames << QLatin1String("(auto)") << QLatin1String("px") << QLatin1String("%"); 0094 0095 QDomElement pathElement = content.firstChildElement(QLatin1String("Path")); 0096 QDomElement displayElement = content.firstChildElement(QLatin1String("Display")); 0097 QDomElement printElement = content.firstChildElement(QLatin1String("Print")); 0098 m_imagePath = pathElement.text(); 0099 m_displaySize.width = displayElement.attribute(QLatin1String("width")).toDouble(); 0100 m_displaySize.height = displayElement.attribute(QLatin1String("height")).toDouble(); 0101 m_displaySize.widthUnit = unitNames.indexOf(displayElement.attribute(QLatin1String("widthUnit"))); 0102 m_displaySize.heightUnit = unitNames.indexOf(displayElement.attribute(QLatin1String("heightUnit"))); 0103 m_useDisplaySizeForPrinting = printElement.attribute(QLatin1String("useDisplaySize")).toInt(); 0104 m_printSize.width = printElement.attribute(QLatin1String("width")).toDouble(); 0105 m_printSize.height = printElement.attribute(QLatin1String("height")).toDouble(); 0106 m_printSize.widthUnit = unitNames.indexOf(printElement.attribute(QLatin1String("widthUnit"))); 0107 m_printSize.heightUnit = unitNames.indexOf(printElement.attribute(QLatin1String("heightUnit"))); 0108 updateEntry(); 0109 } 0110 0111 void ImageEntry::setContentFromJupyter(const QJsonObject& cell) 0112 { 0113 // No need use ImageEntry because without file this entry type are useless 0114 Q_UNUSED(cell); 0115 return; 0116 } 0117 0118 QJsonValue ImageEntry::toJupyterJson() 0119 { 0120 QJsonValue value; 0121 0122 if (!m_imagePath.isEmpty() && m_imageItem) 0123 { 0124 const QImage& image = m_imageItem->pixmap().toImage(); 0125 if (!image.isNull()) 0126 { 0127 QJsonObject entry; 0128 entry.insert(QLatin1String("cell_type"), QLatin1String("markdown")); 0129 0130 QJsonObject metadata; 0131 QJsonObject size; 0132 size.insert(QLatin1String("width"), image.size().width()); 0133 size.insert(QLatin1String("height"), image.size().height()); 0134 metadata.insert(Cantor::JupyterUtils::pngMime, size); 0135 entry.insert(Cantor::JupyterUtils::metadataKey, metadata); 0136 0137 QString text(QLatin1String("<img src='attachment:image.png'>")); 0138 0139 QJsonObject attachments; 0140 attachments.insert(QLatin1String("image.png"), Cantor::JupyterUtils::packMimeBundle(image, Cantor::JupyterUtils::pngMime)); 0141 entry.insert(QLatin1String("attachments"), attachments); 0142 0143 Cantor::JupyterUtils::setSource(entry, text); 0144 0145 value = entry; 0146 } 0147 } 0148 0149 return value; 0150 } 0151 0152 QDomElement ImageEntry::toXml(QDomDocument& doc, KZip* archive) 0153 { 0154 Q_UNUSED(archive); 0155 0156 static QStringList unitNames; 0157 if (unitNames.isEmpty()) 0158 unitNames << QLatin1String("(auto)") << QLatin1String("px") << QLatin1String("%"); 0159 0160 archive->addLocalFile(m_imagePath, QUrl::fromLocalFile(m_imagePath).fileName()); 0161 0162 QDomElement image = doc.createElement(QLatin1String("Image")); 0163 QDomElement path = doc.createElement(QLatin1String("Path")); 0164 QDomElement fileName = doc.createElement(QLatin1String("FileName")); 0165 QDomText pathText = doc.createTextNode(m_imagePath); 0166 QDomText fileNameText = doc.createTextNode(QUrl::fromLocalFile(m_imagePath).fileName()); 0167 path.appendChild(pathText); 0168 fileName.appendChild(fileNameText); 0169 image.appendChild(fileName); 0170 image.appendChild(path); 0171 0172 QDomElement display = doc.createElement(QLatin1String("Display")); 0173 display.setAttribute(QLatin1String("width"), m_displaySize.width); 0174 display.setAttribute(QLatin1String("widthUnit"), unitNames[m_displaySize.widthUnit]); 0175 display.setAttribute(QLatin1String("height"), m_displaySize.height); 0176 display.setAttribute(QLatin1String("heightUnit"), unitNames[m_displaySize.heightUnit]); 0177 image.appendChild(display); 0178 0179 QDomElement print = doc.createElement(QLatin1String("Print")); 0180 print.setAttribute(QLatin1String("useDisplaySize"), m_useDisplaySizeForPrinting); 0181 print.setAttribute(QLatin1String("width"), m_printSize.width); 0182 print.setAttribute(QLatin1String("widthUnit"), unitNames[m_printSize.widthUnit]); 0183 print.setAttribute(QLatin1String("height"), m_printSize.height); 0184 print.setAttribute(QLatin1String("heightUnit"), unitNames[m_printSize.heightUnit]); 0185 image.appendChild(print); 0186 0187 // For the conversion to latex 0188 QDomElement latexSize = doc.createElement(QLatin1String("LatexSizeString")); 0189 QString sizeString; 0190 if (m_useDisplaySizeForPrinting) 0191 sizeString = latexSizeString(m_displaySize); 0192 else 0193 sizeString = latexSizeString(m_printSize); 0194 QDomText latexSizeString = doc.createTextNode(sizeString); 0195 latexSize.appendChild(latexSizeString); 0196 image.appendChild(latexSize); 0197 0198 return image; 0199 } 0200 0201 QString ImageEntry::toPlain(const QString& commandSep, const QString& commentStartingSeq, const QString& commentEndingSeq) 0202 { 0203 Q_UNUSED(commandSep); 0204 0205 return commentStartingSeq + QLatin1String("image: ") + m_imagePath + commentEndingSeq; 0206 } 0207 0208 QString ImageEntry::latexSizeString(const ImageSize& imgSize) 0209 { 0210 // We use the transformation 1 px = 1/72 in ( = 1 pt in Latex) 0211 0212 QString sizeString=QLatin1String(""); 0213 if (imgSize.widthUnit == ImageSize::Auto && 0214 imgSize.heightUnit == ImageSize::Auto) 0215 return QLatin1String(""); 0216 0217 if (imgSize.widthUnit == ImageSize::Percent) { 0218 if (imgSize.heightUnit == ImageSize::Auto || 0219 (imgSize.heightUnit == ImageSize::Percent && 0220 imgSize.width == imgSize.height)) 0221 return QLatin1String("[scale=") + QString::number(imgSize.width / 100) + QLatin1String("]"); 0222 // else? We could set the size based on the actual image size 0223 } else if (imgSize.widthUnit == ImageSize::Auto && 0224 imgSize.heightUnit == ImageSize::Percent) { 0225 return QLatin1String("[scale=") + QString::number(imgSize.height / 100) + QLatin1String("]"); 0226 } 0227 0228 if (imgSize.heightUnit == ImageSize::Pixel) 0229 sizeString = QLatin1String("height=") + QString::number(imgSize.height) + QLatin1String("pt"); 0230 if (imgSize.widthUnit == ImageSize::Pixel) { 0231 if (!sizeString.isEmpty()) 0232 sizeString += QLatin1String(","); 0233 sizeString += QLatin1String("width=") + QString::number(imgSize.width) + QLatin1String("pt"); 0234 } 0235 return QLatin1String("[") + sizeString + QLatin1String("]"); 0236 } 0237 0238 bool ImageEntry::evaluate(EvaluationOption evalOp) 0239 { 0240 evaluateNext(evalOp); 0241 return true; 0242 } 0243 0244 qreal ImageEntry::height() 0245 { 0246 if (m_imageItem && m_imageItem->isVisible()) 0247 return m_imageItem->height(); 0248 else 0249 return m_textItem->height(); 0250 } 0251 0252 void ImageEntry::updateEntry() 0253 { 0254 qreal oldHeight = height(); 0255 if (m_imagePath.isEmpty()) { 0256 m_textItem->setPlainText(i18n("Double click here to configure image settings")); 0257 m_textItem->setVisible(true); 0258 if (m_imageItem) 0259 m_imageItem->setVisible(false); 0260 } 0261 0262 else { 0263 if (!m_imageItem) 0264 m_imageItem = new WorksheetImageItem(this); 0265 0266 // This if-else block was used for backward compability for *cws files 0267 // without FileName tag. After some releases from 20.08 version, it will 0268 // be possible to remove the else part and strip the m_imagePath from the 0269 // code and Path tag from the CWS format 0270 if (!m_fileName.isNull()) { 0271 QString imagePath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + QDir::separator() + m_fileName; 0272 if (imagePath.endsWith(QLatin1String(".eps"), Qt::CaseInsensitive)) { 0273 m_imageItem->setEps(QUrl::fromLocalFile(imagePath)); 0274 } else { 0275 QImage img(imagePath); 0276 m_imageItem->setImage(img); 0277 } 0278 } else { 0279 if (m_imagePath.endsWith(QLatin1String(".eps"), Qt::CaseInsensitive)) { 0280 m_imageItem->setEps(QUrl::fromLocalFile(m_imagePath)); 0281 } else { 0282 QImage img(m_imagePath); 0283 m_imageItem->setImage(img); 0284 } 0285 } 0286 0287 if (!m_imageItem->imageIsValid()) { 0288 const QString msg = i18n("Cannot load image %1", m_imagePath); 0289 m_textItem->setPlainText(msg); 0290 m_textItem->setVisible(true); 0291 m_imageItem->setVisible(false); 0292 } else { 0293 QSizeF size; 0294 if (worksheet()->isPrinting() && ! m_useDisplaySizeForPrinting) 0295 size = imageSize(m_printSize); 0296 else 0297 size = imageSize(m_displaySize); 0298 // Hack: Eps images need to be scaled 0299 if (m_imagePath.endsWith(QLatin1String(".eps"), Qt::CaseInsensitive)) 0300 size /= worksheet()->renderer()->scale(); 0301 m_imageItem->setSize(size); 0302 m_textItem->setVisible(false); 0303 m_imageItem->setVisible(true); 0304 } 0305 } 0306 0307 if (oldHeight != height()) 0308 recalculateSize(); 0309 } 0310 0311 QSizeF ImageEntry::imageSize(const ImageSize& imgSize) 0312 { 0313 const QSize& srcSize = m_imageItem->imageSize(); 0314 qreal w = 0.0; 0315 qreal h = 0.0; 0316 if (imgSize.heightUnit == ImageSize::Percent) 0317 h = srcSize.height() * imgSize.height / 100; 0318 else if (imgSize.heightUnit == ImageSize::Pixel) 0319 h = imgSize.height; 0320 if (imgSize.widthUnit == ImageSize::Percent) 0321 w = srcSize.width() * imgSize.width / 100; 0322 else if (imgSize.widthUnit == ImageSize::Pixel) 0323 w = imgSize.width; 0324 0325 if (imgSize.widthUnit == ImageSize::Auto) { 0326 if (imgSize.heightUnit == ImageSize::Auto) 0327 return QSizeF(srcSize.width(), srcSize.height()); 0328 else if (h == 0) 0329 w = 0; 0330 else 0331 w = h / srcSize.height() * srcSize.width(); 0332 } else if (imgSize.heightUnit == ImageSize::Auto) { 0333 if (w == 0) 0334 h = 0; 0335 else 0336 h = w / srcSize.width() * srcSize.height(); 0337 } 0338 0339 return QSizeF(w,h); 0340 } 0341 0342 void ImageEntry::startConfigDialog() 0343 { 0344 ImageSettingsDialog* dialog = new ImageSettingsDialog(worksheet()->worksheetView()); 0345 dialog->setData(m_imagePath, m_displaySize, m_printSize, 0346 m_useDisplaySizeForPrinting); 0347 connect(dialog, &ImageSettingsDialog::dataChanged, this, &ImageEntry::setImageData); 0348 dialog->show(); 0349 } 0350 0351 void ImageEntry::setImageData(const QString& path, 0352 const ImageSize& displaySize, 0353 const ImageSize& printSize, 0354 bool useDisplaySizeForPrinting) 0355 { 0356 if (path != m_imagePath) { 0357 m_imageWatcher->removePath(m_imagePath); 0358 m_imageWatcher->addPath(path); 0359 m_imagePath = path; 0360 } 0361 0362 m_displaySize = displaySize; 0363 m_printSize = printSize; 0364 m_useDisplaySizeForPrinting = useDisplaySizeForPrinting; 0365 0366 updateEntry(); 0367 } 0368 0369 void ImageEntry::addActionsToBar(ActionBar* actionBar) 0370 { 0371 actionBar->addButton(QIcon::fromTheme(QLatin1String("configure")), i18n("Configure Image"), 0372 this, SLOT(startConfigDialog())); 0373 } 0374 0375 void ImageEntry::layOutForWidth(qreal entry_zone_x, qreal w, bool force) 0376 { 0377 if (size().width() == w && m_textItem->pos().x() == entry_zone_x && !force) 0378 return; 0379 0380 //TODO somethinkg wrong with geometry and control element: control element appears in wrong place 0381 const qreal margin = worksheet()->isPrinting() ? 0 : RightMargin; 0382 0383 double width; 0384 if (m_imageItem && m_imageItem->isVisible()) { 0385 m_imageItem->setGeometry(entry_zone_x, 0, w - margin - entry_zone_x, true); 0386 width = m_imageItem->width(); 0387 } else { 0388 m_textItem->setGeometry(entry_zone_x, 0, w - margin - entry_zone_x, true); 0389 width = m_textItem->width(); 0390 } 0391 0392 setSize(QSizeF(width + margin + entry_zone_x, height() + VerticalMargin)); 0393 } 0394 0395 bool ImageEntry::wantToEvaluate() 0396 { 0397 return false; 0398 } 0399 0400 bool ImageEntry::wantFocus() 0401 { 0402 return false; 0403 } 0404 0405 void ImageEntry::mouseDoubleClickEvent(QGraphicsSceneMouseEvent*) 0406 { 0407 startConfigDialog(); 0408 }