fix: remove white text color filtering, extract all visible text

Only skip text with rendering mode 3 (truly invisible). Removes
fill color tracking that was incorrectly hiding white-on-dark text
in presentations and brochures.
This commit is contained in:
Abimael Martell
2026-03-05 12:06:31 -08:00
parent ead4030f31
commit b126d4b39f
2 changed files with 10 additions and 60 deletions
+7 -59
View File
@@ -109,9 +109,8 @@ pub(crate) fn extract_page_text_items(
// Graphics state tracking // Graphics state tracking
let mut ctm = [1.0f32, 0.0, 0.0, 1.0, 0.0, 0.0]; // Current Transformation Matrix let mut ctm = [1.0f32, 0.0, 0.0, 1.0, 0.0, 0.0]; // Current Transformation Matrix
let mut fill_is_white = false; // Fill color is white (invisible text)
let mut text_rendering_mode: i32 = 0; // 0=fill, 1=stroke, 2=fill+stroke, 3=invisible let mut text_rendering_mode: i32 = 0; // 0=fill, 1=stroke, 2=fill+stroke, 3=invisible
let mut gstate_stack: Vec<([f32; 6], bool, i32)> = Vec::new(); let mut gstate_stack: Vec<([f32; 6], i32)> = Vec::new();
// Text state tracking // Text state tracking
let mut current_font = String::new(); let mut current_font = String::new();
@@ -131,13 +130,12 @@ pub(crate) fn extract_page_text_items(
match op.operator.as_str() { match op.operator.as_str() {
"q" => { "q" => {
// Save graphics state // Save graphics state
gstate_stack.push((ctm, fill_is_white, text_rendering_mode)); gstate_stack.push((ctm, text_rendering_mode));
} }
"Q" => { "Q" => {
// Restore graphics state // Restore graphics state
if let Some((saved_ctm, saved_fill, saved_tr)) = gstate_stack.pop() { if let Some((saved_ctm, saved_tr)) = gstate_stack.pop() {
ctm = saved_ctm; ctm = saved_ctm;
fill_is_white = saved_fill;
text_rendering_mode = saved_tr; text_rendering_mode = saved_tr;
} }
} }
@@ -155,54 +153,6 @@ pub(crate) fn extract_page_text_items(
ctm = multiply_matrices(&new_matrix, &ctm); ctm = multiply_matrices(&new_matrix, &ctm);
} }
} }
"g" => {
// Set grayscale fill color (1.0 = white)
if let Some(gray) = op.operands.first().and_then(get_number) {
fill_is_white = gray > 0.95;
}
}
"rg" => {
// Set RGB fill color
if op.operands.len() >= 3 {
let r = get_number(&op.operands[0]).unwrap_or(0.0);
let g = get_number(&op.operands[1]).unwrap_or(0.0);
let b = get_number(&op.operands[2]).unwrap_or(0.0);
fill_is_white = r > 0.95 && g > 0.95 && b > 0.95;
}
}
"k" => {
// Set CMYK fill color (0,0,0,0 = white)
if op.operands.len() >= 4 {
let c = get_number(&op.operands[0]).unwrap_or(1.0);
let m = get_number(&op.operands[1]).unwrap_or(1.0);
let y = get_number(&op.operands[2]).unwrap_or(1.0);
let k = get_number(&op.operands[3]).unwrap_or(1.0);
fill_is_white = c < 0.05 && m < 0.05 && y < 0.05 && k < 0.05;
}
}
"sc" | "scn" => {
// Set fill color in current color space.
// Infer color model from operand count (ignoring trailing
// pattern-name operands that are not numbers).
// Note: 1-operand form is ambiguous (could be Separation,
// ICCBased, etc.) so we only detect white for 3/4 operands
// where the color model is almost certainly RGB/CMYK.
let nums: Vec<f32> = op.operands.iter().filter_map(get_number).collect();
match nums.len() {
3 => {
fill_is_white = nums[0] > 0.95 && nums[1] > 0.95 && nums[2] > 0.95;
}
4 => {
fill_is_white =
nums[0] < 0.05 && nums[1] < 0.05 && nums[2] < 0.05 && nums[3] < 0.05;
}
_ => {
// 1-operand or unknown — could be any color space,
// assume non-white to avoid hiding visible text.
fill_is_white = false;
}
}
}
"BT" => { "BT" => {
// Begin text block // Begin text block
in_text_block = true; in_text_block = true;
@@ -290,8 +240,8 @@ pub(crate) fn extract_page_text_items(
} }
continue; continue;
} }
// Skip invisible (white/Tr=3) text but still advance text matrix // Skip invisible (Tr=3) text but still advance text matrix
if fill_is_white || text_rendering_mode == 3 { if text_rendering_mode == 3 {
if let Some(w_ts) = w_ts_opt { if let Some(w_ts) = w_ts_opt {
text_matrix[4] += w_ts * text_matrix[0]; text_matrix[4] += w_ts * text_matrix[0];
text_matrix[5] += w_ts * text_matrix[1]; text_matrix[5] += w_ts * text_matrix[1];
@@ -348,8 +298,7 @@ pub(crate) fn extract_page_text_items(
if in_text_block && !op.operands.is_empty() { if in_text_block && !op.operands.is_empty() {
if let Ok(array) = op.operands[0].as_array() { if let Ok(array) = op.operands[0].as_array() {
let font_info = font_widths.get(&current_font); let font_info = font_widths.get(&current_font);
let is_invisible = let is_invisible = text_rendering_mode == 3 || suppress_glyph_extraction;
fill_is_white || text_rendering_mode == 3 || suppress_glyph_extraction;
// Compute space threshold based on font metrics when available // Compute space threshold based on font metrics when available
let space_threshold = if let Some(font_info) = font_info { let space_threshold = if let Some(font_info) = font_info {
@@ -508,8 +457,7 @@ pub(crate) fn extract_page_text_items(
line_matrix[4] += (-tl) * line_matrix[2]; line_matrix[4] += (-tl) * line_matrix[2];
line_matrix[5] += (-tl) * line_matrix[3]; line_matrix[5] += (-tl) * line_matrix[3];
text_matrix = line_matrix; text_matrix = line_matrix;
if !(fill_is_white if !(text_rendering_mode == 3
|| text_rendering_mode == 3
|| suppress_glyph_extraction || suppress_glyph_extraction
|| op.operands.is_empty()) || op.operands.is_empty())
{ {
+3 -1
View File
@@ -1,3 +1,5 @@
**Technical Information**
## l T-12 SI ## l T-12 SI
DuPont Fluorochemicals DuPont Fluorochemicals
@@ -10,7 +12,7 @@ DuPont Fluorochemicals
# Freon 12 # Freon 12
**(R-12)** **(R-12)** **Technical Information** **Technical Information**
**®** **Thermodynamic Properties of Freon 12 Refrigerant** **(R-12)** **SI Units** **®** **Thermodynamic Properties of Freon 12 Refrigerant** **(R-12)** **SI Units**