mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-25 10:39:01 -06:00
123 lines
3.9 KiB
Rust
123 lines
3.9 KiB
Rust
//! A small example that shows how you can extract unicode characters from
|
|
//! glyphs. Please note that Unicode extraction is still experimental, so it
|
|
//! might not work fully correctly in certain cases. Note also that generating
|
|
//! div elements for every single character is clearly not desirable and there
|
|
//! should be some word/sentence merging algorithm in-place, but this is
|
|
//! out-of-scope for this example.
|
|
|
|
use hayro_interpret::font::Glyph;
|
|
use hayro_interpret::{
|
|
BlendMode, ClipPath, Context, Device, GlyphDrawMode, Image, InterpreterCache,
|
|
InterpreterSettings, Paint, PathDrawMode, SoftMask, interpret_page,
|
|
};
|
|
use hayro_syntax::Pdf;
|
|
|
|
use std::fmt::Write;
|
|
|
|
use hayro_cmap::BfString;
|
|
use kurbo::{Affine, BezPath, Point, Rect};
|
|
use std::path::PathBuf;
|
|
|
|
fn main() {
|
|
let args: Vec<String> = std::env::args().collect();
|
|
let relative_path = args
|
|
.get(1)
|
|
.expect("Please provide a relative path to the PDF file as the first argument");
|
|
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(relative_path);
|
|
let data = std::fs::read(path).unwrap();
|
|
|
|
let pdf = Pdf::new(data).unwrap();
|
|
|
|
let settings = InterpreterSettings::default();
|
|
let cache = InterpreterCache::new();
|
|
// Pass dummy values for bbox and initial transform, since we don't care about those.
|
|
let mut context = Context::new(
|
|
Affine::IDENTITY,
|
|
Rect::new(0.0, 0.0, 1.0, 1.0),
|
|
&cache,
|
|
pdf.xref(),
|
|
settings,
|
|
);
|
|
|
|
// Run everything!
|
|
let page = &pdf.pages()[0];
|
|
|
|
let mut extractor = TextExtractor {
|
|
dimensions: page.render_dimensions(),
|
|
..Default::default()
|
|
};
|
|
|
|
writeln!(extractor.text, "<meta charset='utf-8' /> ").unwrap();
|
|
writeln!(extractor.text, "<!-- page {} -->", 0).unwrap();
|
|
writeln!(
|
|
extractor.text,
|
|
"<div id='page{}' style='position: relative; width: {}px; height: {}px; border: 1px black solid'>",
|
|
0,
|
|
extractor.dimensions.0,
|
|
extractor.dimensions.1
|
|
).unwrap();
|
|
|
|
interpret_page(page, &mut context, &mut extractor);
|
|
|
|
writeln!(extractor.text, "</div>").unwrap();
|
|
|
|
print!("{}", extractor.text);
|
|
}
|
|
|
|
#[derive(Default)]
|
|
struct TextExtractor {
|
|
text: String,
|
|
dimensions: (f32, f32),
|
|
}
|
|
|
|
/// Implement `Device` for `TextExtractor`. We extract Unicode text from glyphs.
|
|
impl Device<'_> for TextExtractor {
|
|
fn set_soft_mask(&mut self, _: Option<SoftMask<'_>>) {}
|
|
|
|
fn draw_path(&mut self, _: &BezPath, _: Affine, _: &Paint<'_>, _: &PathDrawMode) {}
|
|
|
|
fn push_clip_path(&mut self, _: &ClipPath) {}
|
|
|
|
fn push_transparency_group(&mut self, _: f32, _: Option<SoftMask<'_>>, _: BlendMode) {}
|
|
|
|
fn draw_glyph(
|
|
&mut self,
|
|
glyph: &Glyph<'_>,
|
|
transform: Affine,
|
|
glyph_transform: Affine,
|
|
_: &Paint<'_>,
|
|
_: &GlyphDrawMode,
|
|
) {
|
|
if let Some(unicode_char) = glyph.as_unicode() {
|
|
// Apply vertical flip transformation to combined transform
|
|
// to place origin at top-left corner.
|
|
let flip_transform = Affine::translate((0.0, self.dimensions.1 as f64))
|
|
* Affine::scale_non_uniform(1.0, -1.0);
|
|
let transform = flip_transform * transform * glyph_transform;
|
|
|
|
let point = Point::new(0.0, 0.0);
|
|
let position = transform * point;
|
|
|
|
writeln!(
|
|
self.text,
|
|
"<div style='position: absolute; color: black; left: {}px; top: {}px; font-size: {}pt'>{}</div>",
|
|
position.x, position.y, 6, match unicode_char {
|
|
BfString::Char(c) => c.to_string(),
|
|
BfString::String(s) => s
|
|
}
|
|
).unwrap();
|
|
} else {
|
|
// Fallback for glyphs without Unicode mapping.
|
|
self.text.push('�');
|
|
}
|
|
}
|
|
|
|
fn pop_clip_path(&mut self) {}
|
|
|
|
fn pop_transparency_group(&mut self) {}
|
|
|
|
fn draw_image(&mut self, _: Image<'_, '_>, _: Affine) {}
|
|
|
|
fn set_blend_mode(&mut self, _: BlendMode) {}
|
|
}
|