Skip to content

Commit 2ce13d8

Browse files
authored
make mojibuster default (#2882)
1 parent 55655ed commit 2ce13d8

25 files changed

Lines changed: 318 additions & 255 deletions

File tree

docs/modules/ROOT/pages/configuration/encoding-detectors.adoc

Lines changed: 117 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -18,44 +18,61 @@
1818
= Configuring Encoding Detectors
1919

2020
Tika uses a chain of _encoding detectors_ to determine the character encoding
21-
of plain text and HTML content. `DefaultEncodingDetector` loads detectors via
22-
the Java service-provider interface (SPI) and runs them in registration order;
23-
the first non-null result wins.
21+
of plain text and HTML content. `DefaultEncodingDetector` discovers detectors
22+
via the Java service-provider interface (SPI, `META-INF/services`).
2423

25-
The default chain is `html-encoding-detector`, `universal-encoding-detector`,
26-
and `icu4j-encoding-detector`.
24+
The chain runs in one of two modes:
25+
26+
* *collect-all* — when a `MetaEncodingDetector` is present (the 4.x default
27+
includes one), every base detector runs and contributes candidate encodings,
28+
then the meta detector picks the best one by decode quality. Registration
29+
order does not matter.
30+
* *first-match-wins* — otherwise, detectors run in registration order and the
31+
first non-null result is used.
2732

2833
== Default Detection Chain
2934

30-
With the stock dependencies on the classpath (the modules
31-
`tika-encoding-detector-html`, `tika-encoding-detector-universal`, and
32-
`tika-encoding-detector-icu4j`):
35+
The stock 4.x distribution registers five detectors:
3336

3437
[cols="1,2,3"]
3538
|===
36-
|Step |Detector |Returns non-null when…
39+
|Detector |Module |Role
40+
41+
|`bom-detector`
42+
|`tika-core`
43+
|Emits a candidate from a leading byte-order mark.
44+
45+
|`metadata-charset-detector`
46+
|`tika-core`
47+
|Emits a candidate from declarative hints (`Content-Type` charset,
48+
`Content-Encoding`) in the `Metadata` object.
3749

38-
|1
3950
|`html-encoding-detector`
40-
|An HTML `<meta charset="…">` or `<meta http-equiv="Content-Type">` tag is
41-
found. Fast lenient regex matcher with a curated subset of WHATWG label
42-
aliases.
51+
|`tika-encoding-detector-html`
52+
|Emits a candidate from an HTML `<meta charset>` / `http-equiv` tag (lenient
53+
regex over a curated subset of WHATWG label aliases).
4354

44-
|2
45-
|`universal-encoding-detector`
46-
|A state-machine structural prober (juniversalchardet fork) recognises the
47-
byte pattern as a known encoding (UTF-8, GB18030, Big5, EUC-JP, several
48-
ISO-8859 variants, etc.).
55+
|`mojibuster-encoding-detector`
56+
|`tika-encoding-detector-mojibuster`
57+
|Byte-bigram Naive Bayes classifier plus structural detectors for UTF-32 and
58+
UTF-16 and a UTF-8 grammar gate.
4959

50-
|3
51-
|`icu4j-encoding-detector`
52-
|ICU4J's `CharsetDetector` returns a match. Catches additional single-byte
53-
encodings (Windows code pages, IBM/EBCDIC variants, etc.).
60+
|`junk-filter-encoding-detector`
61+
|`tika-ml-junkdetect`
62+
|`MetaEncodingDetector` that picks among the other detectors' candidates by
63+
script-aware decode quality. Always runs last.
5464
|===
5565

56-
The chain is permissive — first-match-wins. A declared charset
57-
(e.g. from a `<meta charset>` tag) wins over later structural or statistical
58-
detectors.
66+
Because `junk-filter-encoding-detector` is a `MetaEncodingDetector`, the chain
67+
runs collect-all: detector order is irrelevant, and a declaration (a BOM or a
68+
`<meta charset>` tag) does *not* automatically win. The junk filter will
69+
override a declaration — or even a BOM — when the byte evidence strongly
70+
contradicts it.
71+
72+
NOTE: This is a behaviour change from 3.x, whose default chain was
73+
`html` / `universal` / `icu4j` with first-match-wins (a declaration always
74+
won). `universal-encoding-detector` and `icu4j-encoding-detector` are no longer
75+
in the default distribution; see <<restore-3x,Restore the 3.x chain>>.
5976

6077
== Available Detectors
6178

@@ -66,49 +83,45 @@ referenced by their SPI name in JSON configuration.
6683
|===
6784
|Name |Module |Description
6885

69-
|`html-encoding-detector`
70-
|`tika-encoding-detector-html`
71-
|Fast lenient regex matcher for `<meta charset>` / `http-equiv` tags, with a
72-
curated subset of WHATWG label aliases. Auto-registered (in default chain).
73-
74-
|`universal-encoding-detector`
75-
|`tika-encoding-detector-universal`
76-
|State-machine structural prober (juniversalchardet fork). Auto-registered
77-
(in default chain).
86+
|`bom-detector`
87+
|`tika-core`
88+
|Reads a leading byte-order mark. In the default chain.
7889

79-
|`icu4j-encoding-detector`
80-
|`tika-encoding-detector-icu4j`
81-
|Wraps ICU4J's `CharsetDetector`. Auto-registered (in default chain).
90+
|`metadata-charset-detector`
91+
|`tika-core`
92+
|Reads declarative hints (`Content-Type` charset, `Content-Encoding`) from the
93+
`Metadata` object. In the default chain.
8294

83-
|`standard-html-encoding-detector`
95+
|`html-encoding-detector`
8496
|`tika-encoding-detector-html`
85-
|Spec-strict WHATWG prescan algorithm. Not in the default chain — opt in
86-
explicitly if you need strict WHATWG tokenisation (e.g. ignoring charset
87-
declarations inside HTML comments or other contexts the lenient regex may
88-
match).
97+
|Fast lenient regex matcher for `<meta charset>` / `http-equiv` tags. In the
98+
default chain.
8999

90100
|`mojibuster-encoding-detector`
91101
|`tika-encoding-detector-mojibuster`
92-
|Byte-bigram Naive Bayes classifier plus structural detectors for UTF-32
93-
and UTF-16 and a UTF-8 grammar gate. Not in the default chain — opt in
94-
explicitly.
102+
|Byte-bigram Naive Bayes classifier with structural UTF-32/UTF-16 detectors and
103+
a UTF-8 grammar gate. In the default chain.
95104

96105
|`junk-filter-encoding-detector`
97106
|`tika-ml-junkdetect`
98-
|Text-quality arbitrator (`MetaEncodingDetector`) that picks among other
99-
detectors' candidates by decode quality. Not in the default chain — opt in
100-
explicitly.
107+
|Text-quality arbitrator (`MetaEncodingDetector`). In the default chain; runs
108+
last.
101109

102-
|`bom-detector`
103-
|`tika-core`
104-
|Reads the first 4 bytes for BOM signatures. Helper component, used
105-
internally by `AutoDetectReader`. Not normally added to the SPI chain.
110+
|`standard-html-encoding-detector`
111+
|`tika-encoding-detector-html`
112+
|Spec-strict WHATWG prescan algorithm. Not in the default chain — opt in if you
113+
need strict WHATWG tokenisation (e.g. ignoring charset declarations inside HTML
114+
comments).
106115

107-
|`metadata-charset-detector`
108-
|`tika-core`
109-
|Reads declarative hints (`Content-Type` charset, `Content-Encoding`) from
110-
the `Metadata` object. Helper component, used by parsers that consult
111-
`Content-Type` directly. Not normally added to the SPI chain.
116+
|`universal-encoding-detector`
117+
|`tika-encoding-detector-universal`
118+
|State-machine structural prober (juniversalchardet fork). Not bundled and not
119+
auto-discovered; add the jar and configure it explicitly to use it.
120+
121+
|`icu4j-encoding-detector`
122+
|`tika-encoding-detector-icu4j`
123+
|Wraps ICU4J's `CharsetDetector`. Not bundled and not auto-discovered; add the
124+
jar and configure it explicitly to use it.
112125
|===
113126

114127
== Configuration Examples
@@ -124,83 +137,102 @@ auto-registered detectors:
124137
"encoding-detectors": [
125138
{
126139
"default-encoding-detector": {
127-
"exclude": ["icu4j-encoding-detector"]
140+
"exclude": ["html-encoding-detector"]
128141
}
129142
}
130143
]
131144
}
132145
----
133146

147+
NOTE: Do not combine `default-encoding-detector` with other explicit detector
148+
entries in the same list. When combined, the loader wraps everything in an
149+
outer composite that has no `MetaEncodingDetector` at its top level, so
150+
collect-all arbitration is silently lost and the explicit detectors are never
151+
reached. Use an explicit chain (see below) when you need to configure
152+
individual detectors.
153+
134154
=== Specify the chain explicitly
135155

136-
To replace the SPI-discovered chain with an explicit ordered list:
156+
To replace the SPI-discovered chain with an explicit ordered list. Include
157+
`junk-filter-encoding-detector` (last) to keep collect-all arbitration; omit it
158+
for first-match-wins:
137159

138160
[source,json]
139161
----
140162
{
141163
"encoding-detectors": [
142164
{"html-encoding-detector": {}},
143-
{"universal-encoding-detector": {}}
165+
{"mojibuster-encoding-detector": {}},
166+
{"junk-filter-encoding-detector": {}}
144167
]
145168
}
146169
----
147170

148171
=== Configure the HTML detector's read limit
149172

150-
`html-encoding-detector` reads up to 65 536 bytes by default when scanning
151-
for the `<meta charset>` tag. Raise it if your documents embed large
152-
`<script>` blocks before the meta tag (TIKA-2485):
173+
`html-encoding-detector` reads up to 65 536 bytes by default when scanning for
174+
the `<meta charset>` tag. Raise it if your documents embed large `<script>`
175+
blocks before the meta tag (TIKA-2485). (`mojibuster-encoding-detector` reads a
176+
larger content probe, so in the default chain this limit matters mainly for very
177+
large preambles.)
178+
179+
To configure `markLimit`, specify the full chain explicitly. An explicit list
180+
that includes `junk-filter-encoding-detector` keeps collect-all arbitration; the
181+
configured `html-encoding-detector` participates as a base detector alongside
182+
Mojibuster, and the junk filter arbitrates as usual:
153183

154184
[source,json]
155185
----
156186
{
157187
"encoding-detectors": [
158-
{
159-
"html-encoding-detector": {
160-
"markLimit": 131072
161-
}
162-
},
163-
{"universal-encoding-detector": {}},
164-
{"icu4j-encoding-detector": {}}
188+
{"html-encoding-detector": {"markLimit": 131072}},
189+
{"mojibuster-encoding-detector": {}},
190+
{"junk-filter-encoding-detector": {}}
165191
]
166192
}
167193
----
168194

169195
=== Use the spec-strict WHATWG HTML detector
170196

171-
If your input HTML has charset declarations inside comments (or other
172-
contexts where the lenient regex would false-match), opt in to the
173-
spec-strict prescan:
197+
If your input HTML has charset declarations inside comments (or other contexts
198+
where the lenient regex would false-match), opt in to the spec-strict prescan:
174199

175200
[source,json]
176201
----
177202
{
178203
"encoding-detectors": [
179204
{"standard-html-encoding-detector": {}},
180-
{"universal-encoding-detector": {}},
181-
{"icu4j-encoding-detector": {}}
205+
{"mojibuster-encoding-detector": {}},
206+
{"junk-filter-encoding-detector": {}}
182207
]
183208
}
184209
----
185210

186-
=== Add the Mojibuster + JunkFilter chain (opt-in)
211+
[#restore-3x]
212+
=== Restore the 3.x detection chain (universal + icu4j)
187213

188-
The byte-bigram NB classifier (`mojibuster-encoding-detector`) and the
189-
text-quality arbitrator (`junk-filter-encoding-detector`) are available as
190-
opt-in components. They require the `tika-encoding-detector-mojibuster`
191-
and `tika-ml-junkdetect` modules on the classpath:
214+
The 4.x default no longer bundles or auto-registers
215+
`universal-encoding-detector` and `icu4j-encoding-detector`. To get the legacy
216+
3.x behaviour (`html` / `universal` / `icu4j`, first-match-wins) you must do
217+
*both*:
218+
219+
. *Add the jars to the classpath.* They are no longer in the `tika-app` /
220+
`tika-server-standard` packages, so supply `tika-encoding-detector-universal`
221+
and `tika-encoding-detector-icu4j` yourself (for example via `-Dtika.extras.dir`
222+
— see xref:configuration/index.adoc[the configuration overview]).
223+
. *Configure the chain explicitly.* An explicit chain with no
224+
`MetaEncodingDetector` runs first-match-wins:
192225

193226
[source,json]
194227
----
195228
{
196229
"encoding-detectors": [
197230
{"html-encoding-detector": {}},
198-
{"mojibuster-encoding-detector": {}},
199-
{"junk-filter-encoding-detector": {}}
231+
{"universal-encoding-detector": {}},
232+
{"icu4j-encoding-detector": {}}
200233
]
201234
}
202235
----
203236

204-
`junk-filter-encoding-detector` is a `MetaEncodingDetector` — it collects
205-
candidates from the other detectors and picks the cleanest decoding via a
206-
script-aware text-quality model. It must run last.
237+
Dropping the jars on the classpath alone is *not* enough: unlike the other
238+
detectors, these two are config-only and are not auto-discovered via SPI.

tika-core/src/main/java/org/apache/tika/detect/DefaultEncodingDetector.java

Lines changed: 11 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -17,74 +17,41 @@
1717
package org.apache.tika.detect;
1818

1919
import java.util.Collection;
20-
import java.util.Comparator;
21-
import java.util.HashMap;
22-
import java.util.List;
23-
import java.util.Map;
2420
import javax.imageio.spi.ServiceRegistry;
2521

2622
import org.apache.tika.config.ServiceLoader;
2723

2824
/**
29-
* A composite encoding detector based on all the {@link EncodingDetector}
25+
* A composite encoding detector over all {@link EncodingDetector}
3026
* implementations available through the
3127
* {@link ServiceRegistry service provider mechanism}.
3228
*
33-
* <p>The default chain (Tika 3.x style) runs three detectors in order, with
34-
* the first non-empty result winning:
35-
* <ol>
36-
* <li>{@code org.apache.tika.parser.html.HtmlEncodingDetector}</li>
37-
* <li>{@code org.apache.tika.parser.txt.UniversalEncodingDetector}</li>
38-
* <li>{@code org.apache.tika.parser.txt.Icu4jEncodingDetector}</li>
39-
* </ol>
40-
* Any other {@link EncodingDetector} discovered via SPI (e.g.,
41-
* user-supplied detectors) runs after the three blessed detectors,
42-
* preserving back-compat for callers who add their own.</p>
29+
* <p>The 4.x default chain (via {@code META-INF/services}): BOM and
30+
* metadata-charset detectors (tika-core), the HTML {@code <meta>} detector,
31+
* MojibusterEncodingDetector, and JunkFilterEncodingDetector — a
32+
* {@link MetaEncodingDetector} that arbitrates the candidates by decode quality
33+
* and always runs last, so detector order is irrelevant.</p>
4334
*
44-
* <p>If you need to control the order of the Detectors explicitly, construct
45-
* your own {@link CompositeEncodingDetector} and pass in the list in the
46-
* required order.</p>
35+
* <p>UniversalEncodingDetector and Icu4jEncodingDetector are no longer
36+
* distributed by default and are not auto-discovered; add the jar and enable
37+
* them in a {@code tika-config} to use them.</p>
4738
*
4839
* @since Apache Tika 1.15
4940
*/
5041
public class DefaultEncodingDetector extends CompositeEncodingDetector {
5142

52-
/** Pinned ordering for the 3.x-style default chain. Detectors not on this
53-
* map keep their natural SPI load order behind the three blessed ones. */
54-
private static final Map<String, Integer> PRIORITY = buildPriority();
55-
56-
private static Map<String, Integer> buildPriority() {
57-
Map<String, Integer> p = new HashMap<>();
58-
p.put("org.apache.tika.parser.html.HtmlEncodingDetector", 0);
59-
p.put("org.apache.tika.parser.txt.UniversalEncodingDetector", 1);
60-
p.put("org.apache.tika.parser.txt.Icu4jEncodingDetector", 2);
61-
return p;
62-
}
63-
6443
public DefaultEncodingDetector() {
6544
this(new ServiceLoader(DefaultEncodingDetector.class.getClassLoader()));
6645
}
6746

6847
public DefaultEncodingDetector(ServiceLoader loader) {
69-
super(sorted(loader.loadServiceProviders(EncodingDetector.class)));
48+
super(loader.loadServiceProviders(EncodingDetector.class));
7049
}
7150

7251
public DefaultEncodingDetector(ServiceLoader loader,
7352
Collection<Class<? extends EncodingDetector>>
7453
excludeEncodingDetectors) {
75-
super(sorted(loader.loadServiceProviders(EncodingDetector.class)),
54+
super(loader.loadServiceProviders(EncodingDetector.class),
7655
excludeEncodingDetectors);
7756
}
78-
79-
private static List<EncodingDetector> sorted(List<EncodingDetector> detectors) {
80-
// Pin the 3.x default chain (html, universal, icu4j) to fixed
81-
// positions; other detectors fall to the end with stable secondary
82-
// ordering by class name.
83-
detectors.sort(Comparator
84-
.<EncodingDetector, Integer>comparing(
85-
d -> PRIORITY.getOrDefault(
86-
d.getClass().getName(), Integer.MAX_VALUE))
87-
.thenComparing(d -> d.getClass().getName()));
88-
return detectors;
89-
}
9057
}

tika-core/src/main/resources/META-INF/services/org.apache.tika.detect.EncodingDetector

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,8 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515

16-
# Intentionally empty: tika-core itself does not register any default
17-
# EncodingDetector implementations. The default chain is provided by the
18-
# tika-encoding-detector-html, tika-encoding-detector-universal, and
19-
# tika-encoding-detector-icu4j modules and is sequenced by
20-
# DefaultEncodingDetector.
16+
# tika-core's part of the default 4.x chain; html, mojibuster, and the
17+
# junk-filter arbitrator register from their own modules.
18+
org.apache.tika.detect.BOMDetector
19+
org.apache.tika.detect.MetadataCharsetDetector
2120

0 commit comments

Comments
 (0)