search method
Future<List<UDocSearchHit> >
search(
- String query, {
- UDocSearchOptions options = const UDocSearchOptions(),
Implementation
Future<List<UDocSearchHit>> search(String query, {UDocSearchOptions options = const UDocSearchOptions()}) async {
final UPdfDocument? document = _document;
final String trimmed = query.trim();
if (document == null || trimmed.isEmpty) {
clearSearch();
return const <UDocSearchHit>[];
}
final int token = ++_searchToken;
_searching = true;
liveHits.value = const <UDocSearchHit>[];
emit(value.copyWith(searchQuery: trimmed, isSearching: true, searchHits: const <UDocSearchHit>[], searchHitIndex: -1));
final String needle = options.normalizePersian ? UDocText.forSearch(trimmed) : (options.caseSensitive ? trimmed : trimmed.toLowerCase());
final List<UDocSearchHit> hits = <UDocSearchHit>[];
final int start = options.startPage.clamp(0, document.pageCount);
final int end = options.endPage < 0 ? document.pageCount : options.endPage.clamp(0, document.pageCount);
RegExp? pattern;
if (options.regex) {
try {
pattern = RegExp(trimmed, caseSensitive: options.caseSensitive, multiLine: true);
} on FormatException {
pattern = null;
}
}
for (int page = start; page < end; page++) {
if (token != _searchToken || isDisposed) return hits;
final UDocTextPage text = await textPage(page);
if (text.text.isEmpty) continue;
final UDocNormalizedText normalized = options.normalizePersian
? UDocNormalizedText.build(text.text)
: UDocNormalizedText(options.caseSensitive ? text.text : text.text.toLowerCase(), List<int>.generate(text.text.length, (int i) => i));
final List<List<int>> matches = <List<int>>[];
if (pattern != null) {
for (final RegExpMatch match in pattern.allMatches(text.text)) {
matches.add(<int>[match.start, match.end]);
}
} else {
for (final int index in UDocText.findAll(normalized.text, needle, wholeWord: options.wholeWord)) {
matches.add(<int>[normalized.sourceAt(index), normalized.sourceAt(index + needle.length - 1) + 1]);
}
}
for (final List<int> match in matches) {
final int rawStart = match[0];
final int rawEnd = match[1] > rawStart ? match[1] : rawStart + 1;
final int snippetStart = rawStart - 40 < 0 ? 0 : rawStart - 40;
final int snippetEnd = rawEnd + 40 > text.text.length ? text.text.length : rawEnd + 40;
hits.add(
UDocSearchHit(
pageIndex: page,
start: rawStart,
end: rawEnd,
snippet: text.text.substring(snippetStart, snippetEnd).replaceAll("\n", " "),
rects: text.rectsForRange(rawStart, rawEnd),
matchIndex: hits.length,
),
);
if (hits.length >= options.maxHits) break;
}
liveHits.value = List<UDocSearchHit>.from(hits);
if (hits.length >= options.maxHits) break;
}
if (token != _searchToken || isDisposed) return hits;
_searching = false;
emit(value.copyWith(searchHits: hits, isSearching: false, searchHitIndex: hits.isEmpty ? -1 : 0, pageIndex: hits.isEmpty ? value.pageIndex : hits.first.pageIndex));
return hits;
}