private void SearchResultsListItemControl_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            TextContext.Inlines.Clear();

            // Check that we know what this DataContext is
            PDFSearchResult search_result = DataContext as PDFSearchResult;

            if (null == search_result)
            {
                return;
            }

            string current_context_sentence       = search_result.context_sentence;
            string current_context_sentence_lower = current_context_sentence.ToLower();

            while (!String.IsNullOrEmpty(current_context_sentence))
            {
                // Look for the next keyword
                int next_k           = -1;
                int next_keyword_pos = Int32.MaxValue;
                for (int k = 0; k < search_result.keywords.Length; ++k)
                {
                    int pos = current_context_sentence_lower.IndexOf(search_result.keywords[k]);
                    if (-1 < pos && pos < next_keyword_pos)
                    {
                        next_k           = k;
                        next_keyword_pos = pos;
                    }
                }

                // If we have a keyword
                if (-1 != next_k)
                {
                    // Add a span of everything up to the keyword
                    {
                        Run run = new Run(current_context_sentence.Substring(0, next_keyword_pos));
                        TextContext.Inlines.Add(run);
                    }

                    {
                        Run run = new Run(current_context_sentence.Substring(next_keyword_pos, search_result.keywords[next_k].Length));
                        run.FontWeight = FontWeights.Bold;
                        run.Foreground = TextSearchBrushes.Instance.GetBrushPair(next_k).BorderBrush;
                        TextContext.Inlines.Add(run);
                    }

                    current_context_sentence       = current_context_sentence.Substring(next_keyword_pos + search_result.keywords[next_k].Length);
                    current_context_sentence_lower = current_context_sentence.ToLower();
                }

                else
                {
                    Run run = new Run(current_context_sentence);
                    TextContext.Inlines.Add(run);

                    current_context_sentence       = "";
                    current_context_sentence_lower = "";
                }
            }
        }
        void ListSearchResults_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            PDFSearchResult search_result = ListSearchResults.SelectedItem as PDFSearchResult;

            if (null != SearchSelectionChanged)
            {
                SearchSelectionChanged(search_result);
            }
        }
        void ListSearchResults_MouseUp(object sender, MouseButtonEventArgs e)
        {
            PDFSearchResult search_result = ListSearchResults.SelectedItem as PDFSearchResult;

            if (null != SearchClicked)
            {
                SearchClicked(search_result);
            }
        }
        public static List <PDFSearchResult> SearchPage(PDFDocument pdf_document, int page, string terms, MatchDelegate match)
        {
            // Tidy up the keywords
            if (null == terms)
            {
                terms = "";
            }

            string[] keywords = GenerateIndividualKeywords(terms);

            List <PDFSearchResult> search_results = new List <PDFSearchResult>();
            WordList words = new WordList();
            var      SPLITTER_WHITESPACE = new char[] { ' ', '\n', '\r', '\t' };

            // Add the comments
            {
                if (1 == page && !String.IsNullOrEmpty(pdf_document.Comments))
                {
                    var splits = pdf_document.Comments.Split(SPLITTER_WHITESPACE, StringSplitOptions.RemoveEmptyEntries);
                    foreach (var split in splits)
                    {
                        words.Add(new Word {
                            Text = split
                        });
                    }
                }
            }

            // Add the annotations
            {
                foreach (var pdf_annotation in pdf_document.GetAnnotations())
                {
                    if (page == pdf_annotation.Page)
                    {
                        if (!String.IsNullOrEmpty(pdf_annotation.Text))
                        {
                            var splits = pdf_annotation.Text.Split(SPLITTER_WHITESPACE, StringSplitOptions.RemoveEmptyEntries);
                            foreach (var split in splits)
                            {
                                words.Add(new Word {
                                    Text = split
                                });
                            }
                        }
                    }
                }
            }

            // Add the PDF running text
            {
                WordList words_pdf = pdf_document.GetOCRText(page);
                if (null != words_pdf)
                {
                    words.AddRange(words_pdf);
                }
            }

            // Find the text
            if (null != words && null != keywords)
            {
                // Split keywords
                string[][] split_keywords = new string[keywords.Length][];
                for (int i = 0; i < keywords.Length; ++i)
                {
                    split_keywords[i] = StringTools.Split_NotInDelims(keywords[i].ToLower(), '"', '"', " ").ToArray();
                }

                for (int w = 0; w < words.Count; ++w)
                {
                    Word   first_word       = words[w];
                    string first_word_lower = first_word.Text.ToLower();

                    for (int i = 0; i < split_keywords.Length; ++i)
                    {
                        string[] split_keyword = split_keywords[i];

                        // Ignore spurious empty keyword sets
                        if (1 > split_keyword.Length)
                        {
                            continue;
                        }

                        // Don't process single keywords that are too short
                        if (2 > split_keyword[0].Length)
                        {
                            continue;
                        }

                        // Process the first word - if it doesn't match we are done here
                        if (!match(first_word_lower, split_keyword[0]))
                        {
                            continue;
                        }

                        // If there are more words we have to get a little crafty and check the remaining words
                        bool follows_match = true;
                        for (int j = 0; j < split_keyword.Length; ++j)
                        {
                            if (w + j < words.Count)
                            {
                                Word   follow_word       = words[w + j];
                                string follow_word_lower = follow_word.Text.ToLower();
                                if (!match(follow_word_lower, split_keyword[j]))
                                {
                                    follows_match = false;
                                    break;
                                }
                            }
                            else
                            {
                                follows_match = false;
                                break;
                            }
                        }

                        // If the remaining words dont match, bail
                        if (!follows_match)
                        {
                            continue;
                        }

                        // If we get here, the word (any any follow words) match
                        {
                            PDFSearchResult search_result = new PDFSearchResult();
                            search_results.Add(search_result);

                            // Store the page
                            search_result.keywords = keywords;
                            search_result.page     = page;

                            // Get the words associated with this result
                            {
                                search_result.keyword_index = i;
                                search_result.words         = new Word[split_keyword.Length];
                                for (int j = 0; j < split_keyword.Length; ++j)
                                {
                                    Word follow_word = words[w + j];
                                    search_result.words[j] = follow_word;
                                }
                            }

                            // Create the context sentence
                            {
                                int  MIN_CONTEXT_SIZE = 3;
                                int  MAX_CONTEXT_SIZE = 10;
                                bool ellipsis_start   = false;
                                bool ellipsis_end     = false;
                                int  w_start          = w;
                                while (w_start > 0)
                                {
                                    // Stop at a preceding sentence
                                    if (ContainsASentenceTerminator(words[w_start - 1].Text))
                                    {
                                        if (w - w_start >= MIN_CONTEXT_SIZE)
                                        {
                                            break;
                                        }
                                    }

                                    // Stop if we are going too far
                                    if (w - w_start > MAX_CONTEXT_SIZE)
                                    {
                                        ellipsis_start = true;
                                        break;
                                    }

                                    --w_start;
                                }
                                int w_end = w;
                                while (w_end < words.Count)
                                {
                                    // Stop at the end of a sentence
                                    if (ContainsASentenceTerminator(words[w_end].Text))
                                    {
                                        if (w_end - w >= MIN_CONTEXT_SIZE)
                                        {
                                            break;
                                        }
                                    }

                                    // Stop if we are going too far
                                    if (w_end - w > MAX_CONTEXT_SIZE)
                                    {
                                        ellipsis_end = true;
                                        break;
                                    }


                                    if (w_end + 1 == words.Count)
                                    {
                                        break;
                                    }

                                    ++w_end;
                                }

                                StringBuilder sb = new StringBuilder();
                                sb.AppendFormat("p{0}: ", page);
                                if (ellipsis_start)
                                {
                                    sb.Append("...");
                                }
                                for (int w_current = w_start; w_current <= w_end; ++w_current)
                                {
                                    sb.Append(words[w_current].Text);
                                    sb.Append(" ");
                                }
                                if (ellipsis_end)
                                {
                                    sb.Append("...");
                                }
                                search_result.context_sentence = sb.ToString();
                            }
                        }
                    }
                }
            }

            return(search_results);
        }
        private void ListSearchResults_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            PDFSearchResult search_result = ListSearchResults.SelectedItem as PDFSearchResult;

            SearchSelectionChanged?.Invoke(search_result);
        }
        private void ListSearchResults_MouseUp(object sender, MouseButtonEventArgs e)
        {
            PDFSearchResult search_result = ListSearchResults.SelectedItem as PDFSearchResult;

            SearchClicked?.Invoke(search_result);
        }