package org.terst.feedreader.readability /** * Recognises a cached extraction that still holds raw TeX source instead of rendered maths. * * Needed for the same reason [ConsentNotice] is: fixing the extractor only changes what * FUTURE extractions produce. The reported article ("we should render tex", lcamtuf's * Substack) came with `extractedContent cached: true` -- its raw `\(p = \text{...}\)` * source was already on disk, and every open read it straight back out, so the reader * would have gone on showing TeX source no matter what the extractor learned to do. * Recognising it on read re-extracts the article the next time it is opened, including * everything extracted before maths rendering existed. * * Over-detecting is the real risk here, and it is a specific one: a false positive makes * an article re-extract on EVERY open, for ever, because re-extracting cannot change a * verdict the content itself causes. So the bar is a complete delimiter pair around * something that actually looks like an expression, and never anything that already * carries rendered MathML. */ object UnrenderedTex { /** * Length past which a delimiter pair is read as two unrelated delimiters rather than * one expression. Generous: the reported article's longest block (a ten-line `align` * environment) is well under half this. */ private const val MAX_EXPRESSION_LENGTH = 2_000 /** Delimiter pairs, matching the ones extraction renders (see ReaderExtractor). */ private val DELIMITERS = listOf("\\(" to "\\)", "\\[" to "\\]", "$$" to "$$") /** * Characters no plain sentence between two stray delimiters is likely to contain but * essentially every real expression does -- a command, a group, a sub- or superscript. */ private const val TEX_MARKERS = "\\{_^" /** * Quoted source, not maths: extraction deliberately leaves `
`/`` alone, so
* an article demonstrating TeX would otherwise be convicted on every open and still,
* correctly, come back unchanged.
*/
private val CODE_BLOCK_REGEX = Regex(
"<(pre|code)\\b[^>]*>.*?\\1\\s*>",
setOf(RegexOption.IGNORE_CASE, RegexOption.DOT_MATCHES_ALL)
)
/**
* True when [html] is an extraction whose maths never got rendered.
*
* Rendered maths anywhere in it means extraction already had its go at this page, and
* nothing is to be gained by sending it round again: an expression KaTeX could not
* render is still in there as source and always will be.
*/
fun looksLikeUnrenderedTex(html: String?): Boolean {
if (html.isNullOrBlank() || html.contains("