Browse Source

VTextEdit: support previewing inline images

Le Tan 8 years ago
parent
commit
d2ee3e66d6

+ 33 - 9
src/vimageresourcemanager2.cpp

@@ -23,20 +23,44 @@ bool VImageResourceManager2::contains(const QString &p_name) const
 QSet<int> VImageResourceManager2::updateBlockInfos(const QVector<VBlockImageInfo2> &p_blocksInfo)
 {
     QSet<QString> usedImages;
-    QHash<int, VBlockImageInfo2> newBlocksInfo;
+    QHash<int, QVector<VBlockImageInfo2>> newBlocksInfo;
 
     for (auto const & info : p_blocksInfo) {
-        auto it = newBlocksInfo.insert(info.m_blockNumber, info);
-        VBlockImageInfo2 &newInfo = it.value();
-        if (newInfo.m_padding < 0) {
-            newInfo.m_padding = 0;
+        VBlockImageInfo2 *newInfo = NULL;
+        auto blockIt = newBlocksInfo.find(info.m_blockNumber);
+        if (blockIt == newBlocksInfo.end()) {
+            // New block.
+            QVector<VBlockImageInfo2> vec(1, info);
+            auto it = newBlocksInfo.insert(info.m_blockNumber, vec);
+            newInfo = &it.value().last();
+        } else {
+            // Multiple images for a block.
+            QVector<VBlockImageInfo2> &vec = blockIt.value();
+            int i;
+            for (i = 0; i < vec.size(); ++i) {
+                Q_ASSERT(vec[i].m_blockNumber == info.m_blockNumber);
+                if (info < vec[i]) {
+                    vec.insert(i, info);
+                    newInfo = &vec[i];
+                    break;
+                }
+            }
+
+            if (i == vec.size()) {
+                vec.append(info);
+                newInfo = &vec.last();
+            }
+        }
+
+        if (newInfo->m_padding < 0) {
+            newInfo->m_padding = 0;
         }
 
-        auto imageIt = m_images.find(newInfo.m_imageName);
+        auto imageIt = m_images.find(newInfo->m_imageName);
         if (imageIt != m_images.end()) {
             // Fill the width and height.
-            newInfo.m_imageSize = imageIt.value().size();
-            usedImages.insert(newInfo.m_imageName);
+            newInfo->m_imageSize = imageIt.value().size();
+            usedImages.insert(newInfo->m_imageName);
         }
     }
 
@@ -61,7 +85,7 @@ QSet<int> VImageResourceManager2::updateBlockInfos(const QVector<VBlockImageInfo
     return affectedBlocks;
 }
 
-const VBlockImageInfo2 *VImageResourceManager2::findImageInfoByBlock(int p_blockNumber) const
+const QVector<VBlockImageInfo2> *VImageResourceManager2::findImageInfoByBlock(int p_blockNumber) const
 {
     auto it = m_blocksInfo.find(p_blockNumber);
     if (it != m_blocksInfo.end()) {

+ 4 - 2
src/vimageresourcemanager2.h

@@ -28,7 +28,7 @@ public:
     // Return changed blocks' block number.
     QSet<int> updateBlockInfos(const QVector<VBlockImageInfo2> &p_blocksInfo);
 
-    const VBlockImageInfo2 *findImageInfoByBlock(int p_blockNumber) const;
+    const QVector<VBlockImageInfo2> *findImageInfoByBlock(int p_blockNumber) const;
 
     const QPixmap *findImage(const QString &p_name) const;
 
@@ -39,7 +39,9 @@ private:
     QHash<QString, QPixmap> m_images;
 
     // Image info of all the blocks with image.
-    QHash<int, VBlockImageInfo2> m_blocksInfo;
+    // One block may contain multiple inline images or only one block image.
+    // If there are multiple inline images, they are sorted by the start position.
+    QHash<int, QVector<VBlockImageInfo2>> m_blocksInfo;
 };
 
 #endif // VIMAGERESOURCEMANAGER2_H

+ 3 - 6
src/vpreviewmanager.cpp

@@ -226,11 +226,6 @@ void VPreviewManager::updateBlockImageInfo(const QVector<ImageLinkInfo> &p_image
     for (int i = 0; i < p_imageLinks.size(); ++i) {
         const ImageLinkInfo &link = p_imageLinks[i];
 
-        // Skip inline images.
-        if (!link.m_isBlock) {
-            continue;
-        }
-
         QString name = imageResourceName(link);
         if (name.isEmpty()) {
             continue;
@@ -240,7 +235,9 @@ void VPreviewManager::updateBlockImageInfo(const QVector<ImageLinkInfo> &p_image
                               name,
                               link.m_startPos - link.m_blockPos,
                               link.m_endPos - link.m_blockPos,
-                              link.m_padding);
+                              link.m_padding,
+                              !link.m_isBlock);
+
         blockInfos.push_back(info);
     }
 }

+ 292 - 53
src/vtextdocumentlayout.cpp

@@ -13,6 +13,8 @@
 #include "vimageresourcemanager2.h"
 #include "vtextedit.h"
 
+#define MARKER_THICKNESS        2
+#define MAX_INLINE_IMAGE_HEIGHT 400
 
 VTextDocumentLayout::VTextDocumentLayout(QTextDocument *p_doc,
                                          VImageResourceManager2 *p_imageMgr)
@@ -223,7 +225,9 @@ void VTextDocumentLayout::draw(QPainter *p_painter, const PaintContext &p_contex
                      selections,
                      p_context.clip.isValid() ? p_context.clip : QRectF());
 
-        drawBlockImage(p_painter, block, offset);
+        drawImages(p_painter, block, offset);
+
+        drawMarkers(p_painter, block, offset);
 
         // Draw the cursor.
         int blpos = block.position();
@@ -502,8 +506,6 @@ void VTextDocumentLayout::layoutBlock(const QTextBlock &p_block)
     QTextDocument *doc = document();
     Q_ASSERT(m_margin == doc->documentMargin());
 
-    // The height (y) of the next line.
-    qreal height = 0;
     QTextLayout *tl = p_block.layout();
     QTextOption option = doc->defaultTextOption();
     tl->setTextOption(option);
@@ -521,40 +523,126 @@ void VTextDocumentLayout::layoutBlock(const QTextBlock &p_block)
 
     availableWidth -= (2 * m_margin + extraMargin + m_cursorMargin + m_cursorWidth);
 
-    tl->beginLayout();
+    QVector<Marker> markers;
+    QVector<ImagePaintInfo> images;
+
+    layoutLines(p_block, tl, markers, images, availableWidth, 0);
+
+    // Set this block's line count to its layout's line count.
+    // That is one block may occupy multiple visual lines.
+    const_cast<QTextBlock&>(p_block).setLineCount(p_block.isVisible() ? tl->lineCount() : 0);
+
+    // Update the info about this block.
+    finishBlockLayout(p_block, markers, images);
+}
+
+qreal VTextDocumentLayout::layoutLines(const QTextBlock &p_block,
+                                       QTextLayout *p_tl,
+                                       QVector<Marker> &p_markers,
+                                       QVector<ImagePaintInfo> &p_images,
+                                       qreal p_availableWidth,
+                                       qreal p_height)
+{
+    // Handle block inline image.
+    bool hasInlineImages = false;
+    const QVector<VBlockImageInfo2> *info = NULL;
+    if (m_blockImageEnabled) {
+        info = m_imageMgr->findImageInfoByBlock(p_block.blockNumber());
+
+        if (info
+            && !info->isEmpty()
+            && info->first().m_inlineImage) {
+            hasInlineImages = true;
+        }
+    }
+
+    p_tl->beginLayout();
+
+    int imgIdx = 0;
 
     while (true) {
-        QTextLine line = tl->createLine();
+        QTextLine line = p_tl->createLine();
         if (!line.isValid()) {
             break;
         }
 
         line.setLeadingIncluded(true);
-        line.setLineWidth(availableWidth);
-        height += m_lineLeading;
-        line.setPosition(QPointF(m_margin, height));
-        height += line.height();
+        line.setLineWidth(p_availableWidth);
+        p_height += m_lineLeading;
+
+        if (hasInlineImages) {
+            QVector<const VBlockImageInfo2 *> images;
+            QVector<QPair<qreal, qreal>> imageRange;
+            qreal imgHeight = fetchInlineImagesForOneLine(*info,
+                                                          &line,
+                                                          m_margin,
+                                                          imgIdx,
+                                                          images,
+                                                          imageRange);
+
+            for (int i = 0; i < images.size(); ++i) {
+                layoutInlineImage(images[i],
+                                  p_height,
+                                  imgHeight,
+                                  imageRange[i].first,
+                                  imageRange[i].second,
+                                  p_markers,
+                                  p_images);
+            }
+
+            if (!images.isEmpty()) {
+                p_height += imgHeight + MARKER_THICKNESS + MARKER_THICKNESS;
+            }
+        }
+
+        line.setPosition(QPointF(m_margin, p_height));
+        p_height += line.height();
     }
 
-    tl->endLayout();
+    p_tl->endLayout();
 
-    // Set this block's line count to its layout's line count.
-    // That is one block may occupy multiple visual lines.
-    const_cast<QTextBlock&>(p_block).setLineCount(p_block.isVisible() ? tl->lineCount() : 0);
+    return p_height;
+}
 
-    // Update the info about this block.
-    finishBlockLayout(p_block);
+void VTextDocumentLayout::layoutInlineImage(const VBlockImageInfo2 *p_info,
+                                            qreal p_heightInBlock,
+                                            qreal p_imageSpaceHeight,
+                                            qreal p_xStart,
+                                            qreal p_xEnd,
+                                            QVector<Marker> &p_markers,
+                                            QVector<ImagePaintInfo> &p_images)
+{
+    Marker mk;
+    qreal mky = p_imageSpaceHeight + p_heightInBlock + MARKER_THICKNESS;
+    mk.m_start = QPointF(p_xStart, mky);
+    mk.m_end = QPointF(p_xEnd, mky);
+    p_markers.append(mk);
+
+    if (p_info) {
+        QSize size = p_info->m_imageSize;
+        scaleSize(size, p_xEnd - p_xStart, p_imageSpaceHeight);
+
+        ImagePaintInfo ipi;
+        ipi.m_name = p_info->m_imageName;
+        ipi.m_rect = QRectF(QPointF(p_xStart,
+                                    p_heightInBlock + p_imageSpaceHeight - size.height()),
+                            size);
+        p_images.append(ipi);
+    }
 }
 
-void VTextDocumentLayout::finishBlockLayout(const QTextBlock &p_block)
+void VTextDocumentLayout::finishBlockLayout(const QTextBlock &p_block,
+                                            const QVector<Marker> &p_markers,
+                                            const QVector<ImagePaintInfo> &p_images)
 {
     // Update rect and offset.
     Q_ASSERT(p_block.isValid());
     int num = p_block.blockNumber();
     Q_ASSERT(m_blocks.size() > num);
+    ImagePaintInfo ipi;
     BlockInfo &info = m_blocks[num];
     info.reset();
-    info.m_rect = blockRectFromTextLayout(p_block);
+    info.m_rect = blockRectFromTextLayout(p_block, &ipi);
     Q_ASSERT(!info.m_rect.isNull());
     int pre = previousValidBlockNumber(num);
     if (pre == -1) {
@@ -563,6 +651,30 @@ void VTextDocumentLayout::finishBlockLayout(const QTextBlock &p_block)
         info.m_offset = m_blocks[pre].bottom();
     }
 
+    bool hasImage = false;
+    if (ipi.isValid()) {
+        Q_ASSERT(p_markers.isEmpty());
+        Q_ASSERT(p_images.isEmpty());
+        info.m_images.append(ipi);
+        hasImage = true;
+    } else if (!p_markers.isEmpty()) {
+        // Q_ASSERT(!p_images.isEmpty());
+        info.m_markers = p_markers;
+        info.m_images = p_images;
+        hasImage = true;
+    }
+
+    // Add vertical marker.
+    if (hasImage) {
+        // Fill the marker.
+        // Will be adjusted using offset.
+        Marker mk;
+        mk.m_start = QPointF(-1, 0);
+        mk.m_end = QPointF(-1, info.m_rect.height());
+
+        info.m_markers.append(mk);
+    }
+
     if (info.hasOffset()) {
         fillOffsetFrom(num);
     }
@@ -622,8 +734,13 @@ int VTextDocumentLayout::cursorWidth() const
     return m_cursorWidth;
 }
 
-QRectF VTextDocumentLayout::blockRectFromTextLayout(const QTextBlock &p_block)
+QRectF VTextDocumentLayout::blockRectFromTextLayout(const QTextBlock &p_block,
+                                                    ImagePaintInfo *p_image)
 {
+    if (p_image) {
+        *p_image = ImagePaintInfo();
+    }
+
     QTextLayout *tl = p_block.layout();
     if (tl->lineCount() < 1) {
         return QRectF();
@@ -637,17 +754,29 @@ QRectF VTextDocumentLayout::blockRectFromTextLayout(const QTextBlock &p_block)
         br.setWidth(qMax(br.width(), tl->lineAt(0).naturalTextWidth()));
     }
 
-    // Handle block image.
+    // Handle block non-inline image.
     if (m_blockImageEnabled) {
-        const VBlockImageInfo2 *info = m_imageMgr->findImageInfoByBlock(p_block.blockNumber());
-        if (info && !info->m_imageSize.isNull()) {
-            int maximumWidth = tlRect.width();
-            int padding;
-            QSize size;
-            adjustImagePaddingAndSize(info, maximumWidth, padding, size);
-            int dw = padding + size.width() + m_margin - br.width();
-            int dh = size.height() + m_lineLeading;
-            br.adjust(0, 0, dw > 0 ? dw : 0, dh);
+        const QVector<VBlockImageInfo2> *info = m_imageMgr->findImageInfoByBlock(p_block.blockNumber());
+        if (info && info->size() == 1) {
+            const VBlockImageInfo2& img = info->first();
+            if (!img.m_inlineImage && !img.m_imageSize.isNull()) {
+                int maximumWidth = tlRect.width();
+                int padding;
+                QSize size;
+                adjustImagePaddingAndSize(&img, maximumWidth, padding, size);
+
+                if (p_image) {
+                    p_image->m_name = img.m_imageName;
+                    p_image->m_rect = QRectF(padding + m_margin,
+                                             br.height() + m_lineLeading,
+                                             size.width(),
+                                             size.height());
+                }
+
+                int dw = padding + size.width() + m_margin - br.width();
+                int dh = size.height() + m_lineLeading;
+                br.adjust(0, 0, dw > 0 ? dw : 0, dh);
+            }
         }
     }
 
@@ -729,41 +858,53 @@ void VTextDocumentLayout::adjustImagePaddingAndSize(const VBlockImageInfo2 *p_in
     }
 }
 
-void VTextDocumentLayout::drawBlockImage(QPainter *p_painter,
-                                         const QTextBlock &p_block,
-                                         const QPointF &p_offset)
+void VTextDocumentLayout::drawImages(QPainter *p_painter,
+                                     const QTextBlock &p_block,
+                                     const QPointF &p_offset)
 {
-    if (!m_blockImageEnabled) {
+    if (m_blocks.size() <= p_block.blockNumber()) {
         return;
     }
 
-    const VBlockImageInfo2 *info = m_imageMgr->findImageInfoByBlock(p_block.blockNumber());
-    if (!info || info->m_imageSize.isNull()) {
+    const QVector<ImagePaintInfo> &images = m_blocks[p_block.blockNumber()].m_images;
+    if (images.isEmpty()) {
         return;
     }
 
-    const QPixmap *image = m_imageMgr->findImage(info->m_imageName);
-    Q_ASSERT(image);
+    for (auto const & img : images) {
+        const QPixmap *image = m_imageMgr->findImage(img.m_name);
+        Q_ASSERT(image);
+        QRect targetRect = img.m_rect.adjusted(p_offset.x(),
+                                               p_offset.y(),
+                                               p_offset.x(),
+                                               p_offset.y()).toRect();
+
+        p_painter->drawPixmap(targetRect, *image);
+    }
+}
+
+
+void VTextDocumentLayout::drawMarkers(QPainter *p_painter,
+                                      const QTextBlock &p_block,
+                                      const QPointF &p_offset)
+{
+    if (m_blocks.size() <= p_block.blockNumber()) {
+        return;
+    }
+
+    const QVector<Marker> &markers = m_blocks[p_block.blockNumber()].m_markers;
+    if (markers.isEmpty()) {
+        return;
+    }
 
-    // Draw block image.
-    QTextLayout *tl = p_block.layout();
-    QRectF tlRect = tl->boundingRect();
-    int maximumWidth = tlRect.width();
-    int padding;
-    QSize size;
-    adjustImagePaddingAndSize(info, maximumWidth, padding, size);
-    QRect targetRect(p_offset.x() + padding,
-                     p_offset.y() + tlRect.height() + m_lineLeading,
-                     size.width(),
-                     size.height());
-
-    p_painter->drawPixmap(targetRect, *image);
-
-    // Draw a thin line to link them.
     QPen oldPen = p_painter->pen();
-    QPen newPen(m_imageLineColor, 2, Qt::DashLine);
+    QPen newPen(m_imageLineColor, MARKER_THICKNESS, Qt::DashLine);
     p_painter->setPen(newPen);
-    p_painter->drawLine(QPointF(2, p_offset.y()), QPointF(2, targetRect.bottom()));
+
+    for (auto const & mk : markers) {
+        p_painter->drawLine(mk.m_start + p_offset, mk.m_end + p_offset);
+    }
+
     p_painter->setPen(oldPen);
 }
 
@@ -810,3 +951,101 @@ void VTextDocumentLayout::relayout(const QSet<int> &p_blocks)
 
     updateDocumentSize();
 }
+
+qreal VTextDocumentLayout::fetchInlineImagesForOneLine(const QVector<VBlockImageInfo2> &p_info,
+                                                       const QTextLine *p_line,
+                                                       qreal p_margin,
+                                                       int &p_index,
+                                                       QVector<const VBlockImageInfo2 *> &p_images,
+                                                       QVector<QPair<qreal, qreal>> &p_imageRange)
+{
+    qreal maxHeight = 0;
+    int start = p_line->textStart();
+    int end = p_line->textLength() + start;
+
+    for (int i = 0; i < p_info.size(); ++i) {
+        const VBlockImageInfo2 &img = p_info[i];
+        Q_ASSERT(img.m_inlineImage);
+
+        if (img.m_imageSize.isNull()) {
+            p_index = i + 1;
+            continue;
+        }
+
+        if (img.m_startPos >= start && img.m_startPos < end) {
+            // Start of a new image.
+            qreal startX = p_line->cursorToX(img.m_startPos) + p_margin;
+            qreal endX;
+            if (img.m_endPos <= end) {
+                // End an image.
+                endX = p_line->cursorToX(img.m_endPos) + p_margin;
+                p_images.append(&img);
+                p_imageRange.append(QPair<qreal, qreal>(startX, endX));
+
+                QSize size = img.m_imageSize;
+                scaleSize(size, endX - startX, MAX_INLINE_IMAGE_HEIGHT);
+                if (size.height() > maxHeight) {
+                    maxHeight = size.height();
+                }
+
+                // Image i has been drawn.
+                p_index = i + 1;
+            } else {
+                // This image cross the line.
+                endX = p_line->x() + p_line->width() + p_margin;
+                if (end - img.m_startPos >= ((img.m_endPos - img.m_startPos) >> 1)) {
+                    // Put image at this side.
+                    p_images.append(&img);
+                    p_imageRange.append(QPair<qreal, qreal>(startX, endX));
+
+                    QSize size = img.m_imageSize;
+                    scaleSize(size, endX - startX, MAX_INLINE_IMAGE_HEIGHT);
+                    if (size.height() > maxHeight) {
+                        maxHeight = size.height();
+                    }
+
+                    // Image i has been drawn.
+                    p_index = i + 1;
+                } else {
+                    // Just put a marker here.
+                    p_images.append(NULL);
+                    p_imageRange.append(QPair<qreal, qreal>(startX, endX));
+                }
+
+                break;
+            }
+        } else if (img.m_endPos > start && img.m_startPos < start) {
+            qreal startX = p_line->x() + p_margin;
+            qreal endX = img.m_endPos > end ? p_line->x() + p_line->width()
+                                            : p_line->cursorToX(img.m_endPos);
+            if (p_index <= i) {
+                // Image i has not been drawn. Draw it here.
+                p_images.append(&img);
+                p_imageRange.append(QPair<qreal, qreal>(startX, endX));
+
+                QSize size = img.m_imageSize;
+                scaleSize(size, endX - startX, MAX_INLINE_IMAGE_HEIGHT);
+                if (size.height() > maxHeight) {
+                    maxHeight = size.height();
+                }
+
+                // Image i has been drawn.
+                p_index = i + 1;
+            } else {
+                // Image i has been drawn. Just put a marker here.
+                p_images.append(NULL);
+                p_imageRange.append(QPair<qreal, qreal>(startX, endX));
+            }
+
+            if (img.m_endPos >= end) {
+                break;
+            }
+        } else if (img.m_endPos <= start) {
+            continue;
+        } else {
+            break;
+        }
+    }
+
+    return maxHeight;
+}

+ 85 - 5
src/vtextdocumentlayout.h

@@ -57,6 +57,27 @@ protected:
     void documentChanged(int p_from, int p_charsRemoved, int p_charsAdded) Q_DECL_OVERRIDE;
 
 private:
+    // Denote the start and end position of a marker line.
+    struct Marker
+    {
+        QPointF m_start;
+        QPointF m_end;
+    };
+
+    struct ImagePaintInfo
+    {
+        // The rect to draw the image.
+        QRectF m_rect;
+
+        // Name of the image.
+        QString m_name;
+
+        bool isValid()
+        {
+            return !m_name.isEmpty();
+        }
+    };
+
     struct BlockInfo
     {
         BlockInfo()
@@ -68,6 +89,8 @@ private:
         {
             m_offset = -1;
             m_rect = QRectF();
+            m_markers.clear();
+            m_images.clear();
         }
 
         bool hasOffset() const
@@ -94,10 +117,50 @@ private:
         // The bounding rect of this block, including the margins.
         // Null for invalid.
         QRectF m_rect;
+
+        // Markers to draw for this block.
+        // Y is the offset within this block.
+        QVector<Marker> m_markers;
+
+        // Images to draw for this block.
+        // Y is the offset within this block.
+        QVector<ImagePaintInfo> m_images;
     };
 
     void layoutBlock(const QTextBlock &p_block);
 
+    // Returns the total height of this block after layouting lines and inline
+    // images.
+    qreal layoutLines(const QTextBlock &p_block,
+                      QTextLayout *p_tl,
+                      QVector<Marker> &p_markers,
+                      QVector<ImagePaintInfo> &p_images,
+                      qreal p_availableWidth,
+                      qreal p_height);
+
+    // Layout inline image in a line.
+    // @p_info: if NULL, means just layout a marker.
+    // Returns the image height.
+    void layoutInlineImage(const VBlockImageInfo2 *p_info,
+                           qreal p_heightInBlock,
+                           qreal p_imageSpaceHeight,
+                           qreal p_xStart,
+                           qreal p_xEnd,
+                           QVector<Marker> &p_markers,
+                           QVector<ImagePaintInfo> &p_images);
+
+    // Get inline images belonging to @p_line from @p_info.
+    // @p_index: image [0, p_index) has been drawn.
+    // @p_images: contains all images and markers (NULL element indicates it
+    // is just a placeholder for the marker.
+    // Returns the maximum height of the images.
+    qreal fetchInlineImagesForOneLine(const QVector<VBlockImageInfo2> &p_info,
+                                      const QTextLine *p_line,
+                                      qreal p_margin,
+                                      int &p_index,
+                                      QVector<const VBlockImageInfo2 *> &p_images,
+                                      QVector<QPair<qreal, qreal>> &p_imageRange);
+
     // Clear the layout of @p_block.
     // Also clear all the offset behind this block.
     void clearBlockLayout(QTextBlock &p_block);
@@ -115,7 +178,9 @@ private:
 
     bool validateBlocks() const;
 
-    void finishBlockLayout(const QTextBlock &p_block);
+    void finishBlockLayout(const QTextBlock &p_block,
+                           const QVector<Marker> &p_markers,
+                           const QVector<ImagePaintInfo> &p_images);
 
     int previousValidBlockNumber(int p_number) const;
 
@@ -136,8 +201,11 @@ private:
     void blockRangeFromRectBS(const QRectF &p_rect, int &p_first, int &p_last) const;
 
     // Return a rect from the layout.
+    // If @p_imageRect is not NULL and there is block image for this block, it will
+    // be set to the rect of that image.
     // Return a null rect if @p_block has not been layouted.
-    QRectF blockRectFromTextLayout(const QTextBlock &p_block);
+    QRectF blockRectFromTextLayout(const QTextBlock &p_block,
+                                   ImagePaintInfo *p_image = NULL);
 
     // Update document size when only block @p_blockNumber is changed and the height
     // remain the same.
@@ -150,9 +218,15 @@ private:
 
     // Draw images of block @p_block.
     // @p_offset: the offset for the drawing of the block.
-    void drawBlockImage(QPainter *p_painter,
-                        const QTextBlock &p_block,
-                        const QPointF &p_offset);
+    void drawImages(QPainter *p_painter,
+                    const QTextBlock &p_block,
+                    const QPointF &p_offset);
+
+    void drawMarkers(QPainter *p_painter,
+                     const QTextBlock &p_block,
+                     const QPointF &p_offset);
+
+    void scaleSize(QSize &p_size, int p_width, int p_height);
 
     // Document margin on left/right/bottom.
     qreal m_margin;
@@ -201,4 +275,10 @@ inline void VTextDocumentLayout::setImageLineColor(const QColor &p_color)
     m_imageLineColor = p_color;
 }
 
+inline void VTextDocumentLayout::scaleSize(QSize &p_size, int p_width, int p_height)
+{
+    if (p_size.width() > p_width || p_size.height() > p_height) {
+        p_size.scale(p_width, p_height, Qt::KeepAspectRatio);
+    }
+}
 #endif // VTEXTDOCUMENTLAYOUT_H

+ 13 - 0
src/vtextedit.h

@@ -50,6 +50,19 @@ public:
                && m_imageSize == p_other.m_imageSize;
     }
 
+    bool operator<(const VBlockImageInfo2 &p_other) const
+    {
+        if (m_blockNumber < p_other.m_blockNumber) {
+            return true;
+        } else if (m_blockNumber > p_other.m_blockNumber) {
+            return false;
+        } else if (m_startPos < p_other.m_startPos) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
     QString toString() const
     {
         return QString("VBlockImageInfo2 block %1 start %2 end %3 padding %4 "