using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
namespace PdfiumViewer
{
///
/// Helper class for searching through PDF documents.
///
public class PdfSearchManager
{
private bool _highlightAllMatches;
private PdfMatches _matches;
private List> _bounds;
private int _firstMatch;
private int _offset;
///
/// The renderer associated with the search manager.
///
public PdfRenderer Renderer { get; }
///
/// Gets or sets whether to match case.
///
public bool MatchCase { get; set; }
///
/// Gets or sets whether to match whole words.
///
public bool MatchWholeWord { get; set; }
///
/// Gets or sets the color of matched search terms.
///
public Color MatchColor { get; }
///
/// Gets or sets the border color of matched search terms.
///
public Color MatchBorderColor { get; }
///
/// Gets or sets the border width of matched search terms.
///
public float MatchBorderWidth { get; }
///
/// Gets or sets the color of the current match.
///
public Color CurrentMatchColor { get; }
///
/// Gets or sets the border color of the current match.
///
public Color CurrentMatchBorderColor { get; }
///
/// Gets or sets the border width of the current match.
///
public float CurrentMatchBorderWidth { get; }
///
/// Gets or sets whether all matches should be highlighted.
///
public bool HighlightAllMatches
{
get { return _highlightAllMatches; }
set
{
if (_highlightAllMatches != value)
{
_highlightAllMatches = value;
UpdateHighlights();
}
}
}
///
/// Creates a new instance of the search manager.
///
/// The renderer to create the search manager for.
public PdfSearchManager(PdfRenderer renderer)
{
if (renderer == null)
throw new ArgumentNullException(nameof(renderer));
Renderer = renderer;
HighlightAllMatches = true;
MatchColor = Color.FromArgb(0x80, Color.Yellow);
CurrentMatchColor = Color.FromArgb(0x80, SystemColors.Highlight);
}
///
/// Searches for the specified text.
///
/// The text to search.
/// Whether any matches were found.
public bool Search(string text)
{
Renderer.Markers.Clear();
if (String.IsNullOrEmpty(text))
{
_matches = null;
_bounds = null;
}
else
{
_matches = Renderer.Document.Search(text, MatchCase, MatchWholeWord);
_bounds = GetAllBounds();
}
_offset = -1;
UpdateHighlights();
return _matches != null && _matches.Items.Count > 0;
}
private List> GetAllBounds()
{
var result = new List>();
foreach (var match in _matches.Items)
{
result.Add(Renderer.Document.GetTextBounds(match.TextSpan));
}
return result;
}
///
/// Find the next matched term.
///
/// Whether or not to search forward.
/// False when the first match was found again; otherwise true.
public bool FindNext(bool forward)
{
if (_matches == null || _matches.Items.Count == 0)
return false;
if (_offset == -1)
{
_offset = FindFirstFromCurrentPage();
_firstMatch = _offset;
UpdateHighlights();
ScrollCurrentIntoView();
return true;
}
if (forward)
{
_offset++;
if (_offset >= _matches.Items.Count)
_offset = 0;
}
else
{
_offset--;
if (_offset < 0)
_offset = _matches.Items.Count - 1;
}
UpdateHighlights();
ScrollCurrentIntoView();
return _offset != _firstMatch;
}
private void ScrollCurrentIntoView()
{
var current = _bounds[_offset];
if (current.Count > 0)
Renderer.ScrollIntoView(current[0]);
}
private int FindFirstFromCurrentPage()
{
for (int i = 0; i < Renderer.Document.PageCount; i++)
{
int page = (i + Renderer.Page) % Renderer.Document.PageCount;
for (int j = 0; j < _matches.Items.Count; j++)
{
var match = _matches.Items[j];
if (match.Page == page)
return j;
}
}
return 0;
}
///
/// Resets the search manager.
///
public void Reset()
{
Search(null);
}
private void UpdateHighlights()
{
Renderer.Markers.Clear();
if (_matches == null)
return;
if (_highlightAllMatches)
{
for (int i = 0; i < _matches.Items.Count; i++)
{
AddMatch(i, i == _offset);
}
}
else if (_offset != -1)
{
AddMatch(_offset, true);
}
}
private void AddMatch(int index, bool current)
{
foreach (var pdfBounds in _bounds[index])
{
var bounds = new RectangleF(
pdfBounds.Bounds.Left - 1,
pdfBounds.Bounds.Top + 1,
pdfBounds.Bounds.Width + 2,
pdfBounds.Bounds.Height - 2
);
var marker = new PdfMarker(
pdfBounds.Page,
bounds,
current ? CurrentMatchColor : MatchColor,
current ? CurrentMatchBorderColor : MatchBorderColor,
current ? CurrentMatchBorderWidth : MatchBorderWidth
);
Renderer.Markers.Add(marker);
}
}
}
}