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); } } } }