mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-01-01 02:58:08 +01:00
Issue #116: Navigate to text
This commit is contained in:
parent
a892c192fc
commit
288eead65e
@ -1282,6 +1282,47 @@ void PDFDrawWidgetProxy::goToPage(PDFInteger pageIndex)
|
||||
}
|
||||
}
|
||||
|
||||
void PDFDrawWidgetProxy::goToPageAndEnsureVisible(PDFInteger pageIndex, QRectF ensureVisibleRect)
|
||||
{
|
||||
PDFDrawSpaceController::LayoutItem layoutItem = m_controller->getLayoutItemForPage(pageIndex);
|
||||
|
||||
if (layoutItem.isValid())
|
||||
{
|
||||
// We have found our page, navigate onto it
|
||||
if (isBlockMode())
|
||||
{
|
||||
setBlockIndex(layoutItem.blockIndex);
|
||||
}
|
||||
|
||||
QRectF pageRect = fromDeviceSpace(layoutItem.pageRectMM);
|
||||
QRectF placedRect = pageRect.translated(m_horizontalOffset - m_layout.blockRect.left(), m_verticalOffset - m_layout.blockRect.top());
|
||||
|
||||
const PDFPage* page = m_controller->getDocument()->getCatalog()->getPage(layoutItem.pageIndex);
|
||||
QTransform matrix = QTransform(createPagePointToDevicePointMatrix(page, placedRect));
|
||||
|
||||
QRect wishedRect = matrix.mapRect(ensureVisibleRect).toRect();
|
||||
QRect displayedRect = getWidget()->getDrawWidget()->getWidget()->rect();
|
||||
QPoint topRectPoint = wishedRect.topLeft();
|
||||
|
||||
if (!displayedRect.contains(topRectPoint))
|
||||
{
|
||||
QPoint center = displayedRect.center();
|
||||
QPoint p1(center.x(), topRectPoint.y());
|
||||
|
||||
if (!displayedRect.contains(p1))
|
||||
{
|
||||
scrollByPixels(-QPoint(0, p1.y() - displayedRect.top()));
|
||||
}
|
||||
|
||||
QPoint p2(topRectPoint.x(), center.y());
|
||||
if (!displayedRect.contains(p2))
|
||||
{
|
||||
scrollByPixels(-QPoint(p2.x() - displayedRect.x(), 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PDFDrawWidgetProxy::setPageLayout(PageLayout pageLayout)
|
||||
{
|
||||
if (getPageLayout() != pageLayout)
|
||||
|
@ -288,6 +288,11 @@ public:
|
||||
/// \param pageIndex Page to scroll to
|
||||
void goToPage(PDFInteger pageIndex);
|
||||
|
||||
/// Go to the specified page and ensures point on the page is visible
|
||||
/// \param pageIndex Page to scroll to
|
||||
/// \param ensureVisibleRect Rectangle on page, which should be visible
|
||||
void goToPageAndEnsureVisible(PDFInteger pageIndex, QRectF ensureVisibleRect);
|
||||
|
||||
/// Returns current zoom from widget space to device space. So, for example 2.00 corresponds to 200% zoom,
|
||||
/// and each 1 cm of widget area corresponds to 0.5 cm of the device space area.
|
||||
PDFReal getZoom() const { return m_zoom; }
|
||||
|
@ -966,6 +966,82 @@ void PDFTextBlock::applyTransform(const QTransform& matrix)
|
||||
}
|
||||
}
|
||||
|
||||
QPainterPath PDFTextBlock::getCharacterRangeBoundingPath(const PDFCharacterPointer& start,
|
||||
const PDFCharacterPointer& end,
|
||||
const QTransform& matrix,
|
||||
PDFReal heightIncreaseFactor) const
|
||||
{
|
||||
QPainterPath path;
|
||||
|
||||
PDFTextBlock block = *this;
|
||||
|
||||
// Fix angle of block, so we will get correct selection rectangles (parallel to lines)
|
||||
QTransform angleMatrix;
|
||||
angleMatrix.rotate(block.getAngle());
|
||||
block.applyTransform(angleMatrix);
|
||||
|
||||
const size_t lineStart = start.lineIndex;
|
||||
const size_t lineEnd = end.lineIndex;
|
||||
Q_ASSERT(lineEnd >= lineStart);
|
||||
|
||||
const PDFTextLines& lines = block.getLines();
|
||||
for (size_t lineIndex = lineStart; lineIndex <= lineEnd; ++lineIndex)
|
||||
{
|
||||
if (lineIndex >= lines.size())
|
||||
{
|
||||
// Selection is invalid, do nothing
|
||||
continue;
|
||||
}
|
||||
|
||||
const PDFTextLine& line = lines[lineIndex];
|
||||
const TextCharacters& characters = line.getCharacters();
|
||||
|
||||
if (characters.empty())
|
||||
{
|
||||
// Selection is invalid, do nothing
|
||||
continue;
|
||||
}
|
||||
|
||||
// First determine, which characters will be selected
|
||||
size_t characterStart = 0;
|
||||
size_t characterEnd = characters.size() - 1;
|
||||
|
||||
if (lineIndex == lineStart)
|
||||
{
|
||||
characterStart = start.characterIndex;
|
||||
}
|
||||
if (lineIndex == lineEnd)
|
||||
{
|
||||
characterEnd = end.characterIndex;
|
||||
}
|
||||
|
||||
// Validate indices, then calculate bounding box
|
||||
if (!(characterStart <= characterEnd && characterEnd < characters.size()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
QRectF boundingBox;
|
||||
for (size_t i = characterStart; i <= characterEnd; ++i)
|
||||
{
|
||||
boundingBox = boundingBox.united(characters[i].boundingBox.boundingRect());
|
||||
}
|
||||
|
||||
if (boundingBox.isValid())
|
||||
{
|
||||
// Enlarge height by some percent
|
||||
PDFReal heightAdvance = boundingBox.height() * heightIncreaseFactor * 0.5;
|
||||
boundingBox.adjust(0, -heightAdvance, 0, heightAdvance);
|
||||
path.addRect(boundingBox);
|
||||
}
|
||||
}
|
||||
|
||||
QTransform transformMatrix = angleMatrix.inverted() * matrix;
|
||||
path = transformMatrix.map(path);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
QDataStream& operator>>(QDataStream& stream, PDFTextBlock& block)
|
||||
{
|
||||
stream >> block.m_lines;
|
||||
@ -1459,73 +1535,8 @@ void PDFTextSelectionPainter::draw(QPainter* painter, PDFInteger pageIndex, PDFT
|
||||
continue;
|
||||
}
|
||||
|
||||
PDFTextBlock block = blocks[start.blockIndex];
|
||||
|
||||
// Fix angle of block, so we will get correct selection rectangles (parallel to lines)
|
||||
QTransform angleMatrix;
|
||||
angleMatrix.rotate(block.getAngle());
|
||||
block.applyTransform(angleMatrix);
|
||||
|
||||
QPainterPath path;
|
||||
|
||||
const size_t lineStart = start.lineIndex;
|
||||
const size_t lineEnd = end.lineIndex;
|
||||
Q_ASSERT(lineEnd >= lineStart);
|
||||
|
||||
const PDFTextLines& lines = block.getLines();
|
||||
for (size_t lineIndex = lineStart; lineIndex <= lineEnd; ++lineIndex)
|
||||
{
|
||||
if (lineIndex >= lines.size())
|
||||
{
|
||||
// Selection is invalid, do nothing
|
||||
continue;
|
||||
}
|
||||
|
||||
const PDFTextLine& line = lines[lineIndex];
|
||||
const TextCharacters& characters = line.getCharacters();
|
||||
|
||||
if (characters.empty())
|
||||
{
|
||||
// Selection is invalid, do nothing
|
||||
continue;
|
||||
}
|
||||
|
||||
// First determine, which characters will be selected
|
||||
size_t characterStart = 0;
|
||||
size_t characterEnd = characters.size() - 1;
|
||||
|
||||
if (lineIndex == lineStart)
|
||||
{
|
||||
characterStart = start.characterIndex;
|
||||
}
|
||||
if (lineIndex == lineEnd)
|
||||
{
|
||||
characterEnd = end.characterIndex;
|
||||
}
|
||||
|
||||
// Validate indices, then calculate bounding box
|
||||
if (!(characterStart <= characterEnd && characterEnd < characters.size()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
QRectF boundingBox;
|
||||
for (size_t i = characterStart; i <= characterEnd; ++i)
|
||||
{
|
||||
boundingBox = boundingBox.united(characters[i].boundingBox.boundingRect());
|
||||
}
|
||||
|
||||
if (boundingBox.isValid())
|
||||
{
|
||||
// Enlarge height by some percent
|
||||
PDFReal heightAdvance = boundingBox.height() * HEIGHT_INCREASE_FACTOR * 0.5;
|
||||
boundingBox.adjust(0, -heightAdvance, 0, heightAdvance);
|
||||
path.addRect(boundingBox);
|
||||
}
|
||||
}
|
||||
|
||||
QTransform transformMatrix = angleMatrix.inverted() * matrix;
|
||||
path = transformMatrix.map(path);
|
||||
const PDFTextBlock& block = blocks[start.blockIndex];
|
||||
QPainterPath path = block.getCharacterRangeBoundingPath(start, end, matrix, HEIGHT_INCREASE_FACTOR);
|
||||
|
||||
QColor penColor = item.color.darker();
|
||||
QColor brushColor = item.color;
|
||||
|
@ -32,6 +32,7 @@ namespace pdf
|
||||
{
|
||||
class PDFTextLayout;
|
||||
class PDFTextLayoutStorage;
|
||||
struct PDFCharacterPointer;
|
||||
|
||||
struct PDFTextCharacterInfo
|
||||
{
|
||||
@ -154,6 +155,18 @@ public:
|
||||
|
||||
void applyTransform(const QTransform& matrix);
|
||||
|
||||
/// Retrieves the bounding QPainterPath between two specified character positions within this block.
|
||||
/// The provided character pointers must point to characters within the current block.
|
||||
/// \param start A reference to the PDFCharacterPointer indicating the start character.
|
||||
/// \param end A reference to the PDFCharacterPointer indicating the end character.
|
||||
/// \param matrix Transformation applied to the path
|
||||
/// \param heightIncreaseFactor Height increase factor for characters
|
||||
/// \return QPainterPath representing the bounding path between the start and end characters.
|
||||
QPainterPath getCharacterRangeBoundingPath(const PDFCharacterPointer& start,
|
||||
const PDFCharacterPointer& end,
|
||||
const QTransform& matrix,
|
||||
PDFReal heightIncreaseFactor) const;
|
||||
|
||||
friend QDataStream& operator<<(QDataStream& stream, const PDFTextBlock& block);
|
||||
friend QDataStream& operator>>(QDataStream& stream, PDFTextBlock& block);
|
||||
|
||||
|
@ -315,6 +315,25 @@ void PDFFindTextTool::clearResults()
|
||||
getProxy()->repaintNeeded();
|
||||
}
|
||||
|
||||
void PDFFindTextTool::goToCurrentResult()
|
||||
{
|
||||
PDFTextSelection textSelection = getTextSelectionSelectedResultOnly();
|
||||
if (!textSelection.isEmpty())
|
||||
{
|
||||
const PDFTextSelectionColoredItem& firstItem = *textSelection.begin();
|
||||
PDFTextLayoutGetter textLayoutGetter = getProxy()->getTextLayoutCompiler()->getTextLayoutLazy(firstItem.start.pageIndex);
|
||||
|
||||
pdf::PDFTextSelectionPainter textSelectionPainter(&textSelection);
|
||||
QPainterPath painterPath = textSelectionPainter.prepareGeometry(firstItem.start.pageIndex, textLayoutGetter, QTransform(), nullptr);
|
||||
|
||||
if (!painterPath.isEmpty())
|
||||
{
|
||||
getProxy()->goToPageAndEnsureVisible(firstItem.start.pageIndex, painterPath.boundingRect());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PDFFindTextTool::setActiveImpl(bool active)
|
||||
{
|
||||
BaseClass::setActiveImpl(active);
|
||||
@ -327,7 +346,7 @@ void PDFFindTextTool::setActiveImpl(bool active)
|
||||
getProxy()->getTextLayoutCompiler()->makeTextLayout();
|
||||
|
||||
// Create dialog
|
||||
m_dialog = new PDFFindTextToolDialog(getProxy(), m_parentDialog, Qt::Popup | Qt::CustomizeWindowHint | Qt::WindowTitleHint);
|
||||
m_dialog = new PDFFindTextToolDialog(getProxy(), m_parentDialog, Qt::Popup | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint);
|
||||
m_dialog->setWindowTitle(tr("Find"));
|
||||
|
||||
QGridLayout* layout = new QGridLayout(m_dialog);
|
||||
@ -448,7 +467,7 @@ void PDFFindTextTool::onActionPrevious()
|
||||
}
|
||||
m_textSelection.dirty();
|
||||
getProxy()->repaintNeeded();
|
||||
getProxy()->goToPage(m_findResults[m_selectedResultIndex].textSelectionItems.front().first.pageIndex);
|
||||
goToCurrentResult();
|
||||
updateTitle();
|
||||
}
|
||||
}
|
||||
@ -460,7 +479,7 @@ void PDFFindTextTool::onActionNext()
|
||||
m_selectedResultIndex = (m_selectedResultIndex + 1) % m_findResults.size();
|
||||
m_textSelection.dirty();
|
||||
getProxy()->repaintNeeded();
|
||||
getProxy()->goToPage(m_findResults[m_selectedResultIndex].textSelectionItems.front().first.pageIndex);
|
||||
goToCurrentResult();
|
||||
updateTitle();
|
||||
}
|
||||
}
|
||||
@ -591,6 +610,20 @@ PDFTextSelection PDFFindTextTool::getTextSelectionImpl() const
|
||||
return result;
|
||||
}
|
||||
|
||||
PDFTextSelection PDFFindTextTool::getTextSelectionSelectedResultOnly() const
|
||||
{
|
||||
pdf::PDFTextSelection result;
|
||||
|
||||
if (m_selectedResultIndex < m_findResults.size())
|
||||
{
|
||||
const pdf::PDFFindResult& findResult = m_findResults[m_selectedResultIndex];
|
||||
result.addItems(findResult.textSelectionItems, Qt::transparent);
|
||||
}
|
||||
result.build();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
PDFSelectTextTool::PDFSelectTextTool(PDFDrawWidgetProxy* proxy, QAction* action, QAction* copyTextAction, QAction* selectAllAction, QAction* deselectAction, QObject* parent) :
|
||||
BaseClass(proxy, action, parent),
|
||||
m_copyTextAction(copyTextAction),
|
||||
|
@ -183,6 +183,7 @@ private:
|
||||
void updateResultsUI();
|
||||
void updateTitle();
|
||||
void clearResults();
|
||||
void goToCurrentResult();
|
||||
|
||||
QAction* m_prevAction;
|
||||
QAction* m_nextAction;
|
||||
@ -201,6 +202,7 @@ private:
|
||||
|
||||
pdf::PDFTextSelection getTextSelection() const { return m_textSelection.get(this, &PDFFindTextTool::getTextSelectionImpl); }
|
||||
pdf::PDFTextSelection getTextSelectionImpl() const;
|
||||
pdf::PDFTextSelection getTextSelectionSelectedResultOnly() const;
|
||||
|
||||
struct SearchParameters
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user