Sitefinity's document search indexes the contents of uploaded files through ITextExtractor
implementations. Out of the box it ships extractors for exactly five MIME types: PDF, DOCX, HTML,
plain text and RTF. Upload a PowerPoint deck or a spreadsheet and only the title is searchable.
These are drop-in replacements and additions, plus the failure handling you need to survive a real document library. Everything here targets .NET Framework 4.8 and the OpenXML SDK that ships with Sitefinity (2.0.5022.0, the original 2008 release), because that is what is on the box.
| File | What it is |
|---|---|
PdfTextExtractor.cs |
Replaces the stock PDF extractor, which fails on PDFs whose structure tree throws during import |
PptxTextExtractor.cs |
New: pptx / pptm / ppsx, including speaker notes |
XlsxTextExtractor.cs |
New: xlsx / xlsm / xltx, dereferencing the shared-string table |
WordTextExtractor.cs |
New: docm (macro-enabled Word), which the built-in docx extractor is not keyed to |
OpenXmlPackageReader.cs |
Shared open policy for the three OOXML extractors, and where all the SDK workarounds live |
OpenXmlPackageSanitizer.cs |
Strips printerSettings parts so a deck the SDK refuses can be retried |
FileSignature.cs |
Magic-number sniffing, so unreadable input is skipped instead of reported |
TextExtractorOutput.cs |
Writing text back out, and building diagnostic context |
ExtractorGuard.cs |
Containment: one failure costs one document's body text, not the whole index |
DocumentServiceConfig.config |
The DocumentServiceConfig.config entries that activate all of this |
Namespace is Sitefinity.TextExtractors throughout. Change it to whatever you use.
These are not hypothetical. Each one was found in production, in this order, each hidden behind the previous one.
1. The stock PDF extractor gives up too early. It attaches its exception-tolerant handler to
the document after Import() returns, which is too late for failures thrown during import
(InvalidStructureTreeException, RichMedia annotations). Subscribing on ImportSettings instead
handles them at import time, and IgnoreMarkedContent skips the structure tree entirely. Text
extraction never needs it.
2. The SDK cannot open Office files containing printer settings. OpenXML SDK 2.0's content-type
table maps every printerSettings part to the spreadsheet printer-settings content type, so
a PowerPoint deck saved by a machine with a printer configured throws:
The document cannot be opened because there is an invalid part with an unexpected content type.
[Part Uri=/ppt/printerSettings/printerSettings1.bin],
[Content Type=application/vnd.openxmlformats-officedocument.presentationml.printerSettings],
[Expected Content Type=application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings].
Read the last two lines: the part's content type is correct, the SDK's expectation is wrong.
Fixed in SDK 2.5+, but you generally cannot swap the DLL out from under Sitefinity's own dependency.
OpenXmlPackageSanitizer removes those parts from an in-memory copy and the extractor retries.
3. Sanitizing throws on any document with a hyperlink. PackUriHelper.ResolvePartUri throws
ArgumentException("Cannot be an absolute URI") when handed an external relationship, which is
what an ordinary hyperlink is. The first version of the sanitizer above worked perfectly on a
synthetic test file and failed on every real document, because real documents have links. Hence
IsInternalTarget.
4. The SDK's own failure cleanup throws and hides the real error. When Load() fails it calls
Close() → DeleteUnusedDataPartOnClose() → Package.DeletePart(), which throws
IOException("Cannot modify a read-only container") because the package was opened read-only.
That second exception replaces the first, so catch (OpenXmlPackageException) never matches and
your recovery never runs. Worse, the same cleanup runs on successful disposal too, which would
discard text you already extracted. Hence IsRecoverableOpenFailure catching IOException as
well, and DisposeQuietly.
Telerik runs every inbound pipe through PublishingHelper.ForEachSafe, which swallows exceptions
and silently drops the item from the index. An unguarded NullReferenceException in a document
pipe will quietly unindex documents for as long as it takes someone to notice.
ExtractorGuard.Run caps the blast radius at one document's body text, and reports with enough
context to act on. The per-step cap matters: a systemic data problem during a full reindex fails
once per item, and 20,000 identical error reports is the same as none.
Add to App_Data/Sitefinity/Configuration/DocumentServiceConfig.config. Sitefinity merges these
additively with its built-in registrations, so listing only what you are changing is enough.
See DocumentServiceConfig.config.
Two things worth knowing:
- Registering an extractor does nothing to documents already in the index. Deploy, add the config, then reindex.
- Sitefinity picks the extractor from the document's stored MIME type, which is derived from the
file extension at upload time and never from the bytes. A
.xlsrenamed to.xlsx, or any password-protected Office file (encryption wraps the whole OOXML package in an OLE2 compound file), will reach these extractors without being a zip. That is whatFileSignatureis for.
Legacy binary Office formats (.ppt, .doc, .xls) are not covered. They are not ZIP
packages, so the OpenXML SDK cannot read them at all and neither can these extractors. If you need
them, NPOI reads all three.
Scanned PDFs have no text layer, so extraction succeeds with an empty string and the document
indexes title-only. PdfTextExtractor marks the seam where OCR would go, with the caveats worth
reading before you enable it.