From 6371bf1ec809446f1d41f044215015efe0a9b217 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Sch=C3=BCtt?= Date: Fri, 15 Nov 2024 21:00:40 +0100 Subject: [PATCH] Update img component --- .DS_Store | Bin 14340 -> 10244 bytes Cargo.lock | 12 ++++----- Cargo.toml | 22 +++++----------- examples/html.rs | 46 +++++++++++++++++++++++++++++++-- src/html.rs | 42 ++++++++++++++++++++++-------- src/image.rs | 65 ++++++++++++++++++++++++++++++++++++++++++++++- src/lib.rs | 2 +- src/wasm.rs | 3 ++- 8 files changed, 155 insertions(+), 37 deletions(-) diff --git a/.DS_Store b/.DS_Store index c1b8113faa6690834e0c000561627358a23630d3..a9c2a4e79c26d8e7725c1a661908bb4fab144f11 100644 GIT binary patch delta 169 zcmZoEXbF&DU|?W$DortDU{C-uIe-{M3-C-V6q~3gIoZI3MHI+qkO5*wpg2QLx?yl~ zes00Wi)QQ-9~5q8=V0Mrl-bOqP{PQWQkuiF|AhUp0a03ZfkTn}GerKM{Z=%b{FxiAhd2)gYJEsOn1<)>o$vGwyH{VfV GW(EKOdnQr< delta 1482 zcma)6T})e596!HHfqT7WoGZ4_TN$0L4n~0%lEQ>B7%~jVK3EBbF|p;|!5yV7z2##= z#0UH~F?eI*7PCYrem$75Mzh5jjXI-7XJ>U0Q^2N&O}3|mM9C~kkvLI_ zz8>`&0iSJ$8DL|RXU3(Q0~CES9!nacr>C-CKE@rQ>t-Q8myS0bY547)Oa+b zEvWZJGpcS(I2^0qF7r0n5Z*3xZujo=2Se>$z5NG*M;6qyt|b$6z%s6G>)0d8k;&xjEc+N;g1->ly-$`O zG>?8Ds%H+S)C6nmLgD=VeX=~JPe;@0!Nr7{rk@q;1I#`_%kv&Htyj@9$ux83FU2Dt z=J`>xWVfQIN=~YBRURn{%d)XiaaXZ_;CCVHhKbU=%S-LBk2m zAcIqQ7%O-LPvAMM<9Qf(2^)9=Z{jUvPv9ebf=l=mU*lVRhwpI(KjAw5#9y3^E9Oc# z%DFf{*T^+-0WQeh%kAa5Oac+YnJ>f~?N_l6lk)Fs{onUmnD#@(oASer;Uqa7XS z#9nlx9|IV~0fzc0#xRa!n7}kceFkwPu!tp`#4^_K7(@O^Jk4-_0Wab-&UD}$Ud3y8 z-J$?4;G)&yzlaWOVQEQx!DIo^Y>n$$$!6*-AY|<{gY1TMz9jpltDPCoz*THz1Vko# rZ~pIYMt>
", - "

Hello!

", + "

Hello!

", ]; +pub struct ImgComponent { } + +impl XmlComponentTrait for ImgComponent { + + fn get_available_arguments(&self) -> ComponentArguments { + ComponentArguments { + accepts_text: false, + args: vec![("src".to_string(), "String".to_string())] + } + } + + fn render_dom( + &self, + components: &XmlComponentMap, + arguments: &FilteredComponentArguments, + content: &XmlTextContent, + ) -> Result { + // TODO: parse image from arguments["src"] + Ok(Dom::image( + InternalImageRef::new_rawimage( + translate_to_internal_rawimage( + &RawImage::decode_from_bytes(include_bytes!("./assets/img/dog_alpha.png")).unwrap() + ) + ).unwrap() + ).style(CssApiWrapper::empty())) + } +} + fn main() -> Result<(), String> { + for (i, h) in HTML_STRINGS.iter().enumerate() { + + let components = vec![XmlComponent { + id: "img".to_string(), + renderer: Box::new(ImgComponent { }), + inherit_vars: false, + }]; + + let config = XmlRenderOptions { + components, + .. Default::default() + }; + let doc = PdfDocument::new("HTML rendering demo") - .with_html(h, &XmlRenderOptions::default())? + .with_html(h, config)? .save(&PdfSaveOptions::default()); std::fs::write(format!("html{i}.pdf"), doc).unwrap(); } + Ok(()) } diff --git a/src/html.rs b/src/html.rs index 7a493d2..b2fc46b 100644 --- a/src/html.rs +++ b/src/html.rs @@ -1,8 +1,9 @@ use crate::{BuiltinFont, Mm, Op, PdfDocument, PdfPage, PdfResources, Pt}; +use crate::{BuiltinFont, Mm, Op, PdfDocument, PdfPage, PdfResources, Pt}; use azul_core::{ app_resources::{ - DpiScaleFactor, Epoch, IdNamespace, ImageCache, ImageDescriptor, ImageRef, ImageRefHash, - RendererResources, + DpiScaleFactor, Epoch, IdNamespace, ImageCache, + ImageDescriptor, ImageRef, ImageRefHash, RendererResources }, callbacks::DocumentId, display_list::{ @@ -10,15 +11,29 @@ use azul_core::{ StyleBorderStyles, StyleBorderWidths, }, dom::{NodeData, NodeId}, - styled_dom::{ContentGroup, DomId, StyledDom, StyledNode}, + styled_dom::{ContentGroup, DomId, StyledNode}, ui_solver::LayoutResult, - window::{FullWindowState, LogicalSize}, - xml::{XmlComponentMap, XmlNode}, + window::{FullWindowState, LogicalSize}, xml::Xml, }; use azul_css::{CssPropertyValue, FloatValue, LayoutDisplay, StyleTextColor, U8Vec}; use base64::Engine; use rust_fontconfig::{FcFont, FcFontCache, FcPattern}; use std::collections::BTreeMap; +pub use azul_core::xml::{ + XmlComponent, + XmlComponentTrait, + ComponentArguments, + XmlComponentMap, + FilteredComponentArguments, + XmlTextContent, + RenderDomError, + CompileError, + XmlNode +}; +pub use azul_core::styled_dom::StyledDom; +pub use azul_core::dom::Dom; +pub use azul_core::app_resources::ImageRef as InternalImageRef; +pub use azul_css_parser::CssApiWrapper; const DPI_SCALE: DpiScaleFactor = DpiScaleFactor { inner: FloatValue::const_new(1), @@ -32,12 +47,13 @@ const DOCUMENT_ID: DocumentId = DocumentId { pub type Base64String = String; -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug)] pub struct XmlRenderOptions { pub images: BTreeMap, pub fonts: BTreeMap, pub page_width: Mm, pub page_height: Mm, + pub components: Vec, } impl Default for XmlRenderOptions { @@ -47,15 +63,17 @@ impl Default for XmlRenderOptions { fonts: Default::default(), page_width: Mm(210.0), page_height: Mm(297.0), + components: Default::default(), } } } pub(crate) fn xml_to_pages( file_contents: &str, - config: &XmlRenderOptions, + config: XmlRenderOptions, document: &mut PdfDocument, ) -> Result, String> { + let size = LogicalSize { width: config.page_width.into_pt().0, height: config.page_height.into_pt().0, @@ -66,8 +84,13 @@ pub(crate) fn xml_to_pages( let fixup = fixup_xml_nodes(&root_nodes); - let styled_dom = azul_core::xml::str_to_dom(&fixup, &mut XmlComponentMap::default()) - .map_err(|e| format!("Error constructing DOM: {e}"))?; + let mut components = XmlComponentMap::default(); + for c in config.components { + components.register_component(c); + } + + let styled_dom = azul_core::xml::str_to_dom(fixup.as_ref(), &mut components) + .map_err(|e| format!("Error constructing DOM: {}", e.to_string()))?; let dom_id = DomId { inner: 0 }; let mut fake_window_state = FullWindowState::default(); @@ -447,7 +470,6 @@ fn displaylist_handle_rect( } if let Some((text, id, color)) = opt_text { - println!("writing text {text:#?} {id:?}"); ops.push(Op::StartTextSection); ops.push(Op::SetFillColor { col: crate::Color::Rgb(crate::Rgb { diff --git a/src/image.rs b/src/image.rs index 7ebfcc8..a5ba1f2 100644 --- a/src/image.rs +++ b/src/image.rs @@ -99,6 +99,69 @@ impl RawImage { pub fn decode_from_bytes(bytes: &[u8]) -> Result { use image::DynamicImage::*; + let im = image::guess_format(bytes).map_err(|e| e.to_string())?; + let b_len = bytes.len(); + + #[cfg(not(feature = "gif"))] { + let err = format!("cannot decode image (len = {b_len} bytes): printpdf is missing feature 'gif' to decode GIF files. Please enable it or construct the RawImage manually."); + if im == image::ImageFormat::Gif { return Err(err); } + } + + #[cfg(not(feature = "jpeg"))] { + let err = format!("cannot decode image (len = {b_len} bytes): printpdf is missing feature 'jpeg' to decode JPEG files. Please enable it or construct the RawImage manually."); + if im == image::ImageFormat::Gif { return Err(err); } + } + + #[cfg(not(feature = "png"))] { + let err = format!("cannot decode image (len = {b_len} bytes): printpdf is missing feature 'png' to decode PNG files. Please enable it or construct the RawImage manually."); + if im == image::ImageFormat::Png { return Err(err); } + } + + #[cfg(not(feature = "pnm"))] { + let err = format!("cannot decode image (len = {b_len} bytes): printpdf is missing feature 'pnm' to decode PNM files. Please enable it or construct the RawImage manually."); + if im == image::ImageFormat::Pnm { return Err(err); } + } + + #[cfg(not(feature = "tiff"))] { + let err = format!("cannot decode image (len = {b_len} bytes): printpdf is missing feature 'tiff' to decode TIFF files. Please enable it or construct the RawImage manually."); + if im == image::ImageFormat::Tiff { return Err(err); } + } + + #[cfg(not(feature = "tiff"))] { + let err = format!("cannot decode image (len = {b_len} bytes): printpdf is missing feature 'tiff' to decode TIFF files. Please enable it or construct the RawImage manually."); + if im == image::ImageFormat::Tiff { return Err(err); } + } + + #[cfg(not(feature = "bmp"))] { + let err = format!("cannot decode image (len = {b_len} bytes): printpdf is missing feature 'bmp' to decode BMP files. Please enable it or construct the RawImage manually."); + if im == image::ImageFormat::Bmp { return Err(err); } + } + + #[cfg(not(feature = "ico"))] { + let err = format!("cannot decode image (len = {b_len} bytes): printpdf is missing feature 'ico' to decode ICO files. Please enable it or construct the RawImage manually."); + if im == image::ImageFormat::Ico { return Err(err); } + } + + #[cfg(not(feature = "tga"))] { + let err = format!("cannot decode image (len = {b_len} bytes): printpdf is missing feature 'tga' to decode TGA files. Please enable it or construct the RawImage manually."); + if im == image::ImageFormat::Tga { return Err(err); } + } + + #[cfg(not(feature = "hdr"))] { + let err = format!("cannot decode image (len = {b_len} bytes): printpdf is missing feature 'hdr' to decode HDR files. Please enable it or construct the RawImage manually."); + if im == image::ImageFormat::Hdr { return Err(err); } + } + + #[cfg(not(feature = "dds"))] { + let err = format!("cannot decode image (len = {b_len} bytes): printpdf is missing feature 'dds' to decode DDS files. Please enable it or construct the RawImage manually."); + if im == image::ImageFormat::Dds { return Err(err); } + } + + #[cfg(not(feature = "webp"))] { + let err = format!("cannot decode image (len = {b_len} bytes): printpdf is missing feature 'webp' to decode WEBP files. Please enable it or construct the RawImage manually."); + if im == image::ImageFormat::WebP { return Err(err); } + } + let im = image::ImageReader::new(Cursor::new(bytes)) .with_guessed_format() .map_err(|e| e.to_string())? @@ -269,7 +332,7 @@ pub(crate) fn translate_from_internal_rawimage( } } -pub(crate) fn translate_to_internal_rawimage(im: &RawImage) -> azul_core::app_resources::RawImage { +pub fn translate_to_internal_rawimage(im: &RawImage) -> azul_core::app_resources::RawImage { azul_core::app_resources::RawImage { pixels: match &im.pixels { RawImageData::U8(vec) => azul_core::app_resources::RawImageData::U8(vec.clone().into()), diff --git a/src/lib.rs b/src/lib.rs index 845cc31..485e8e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -198,7 +198,7 @@ impl PdfDocument { pub fn with_html( &mut self, html: &str, - config: &XmlRenderOptions, + config: XmlRenderOptions, ) -> Result<&mut Self, String> { let mut pages = crate::html::xml_to_pages(html, config, self)?; self.pages.append(&mut pages); diff --git a/src/wasm.rs b/src/wasm.rs index 4f0d5e7..ed938de 100644 --- a/src/wasm.rs +++ b/src/wasm.rs @@ -74,10 +74,11 @@ fn printpdf_from_xml_internal( page_height: Mm(input.options.page_height_mm.unwrap_or(297.0)), images: BTreeMap::new(), fonts: BTreeMap::new(), + components: Vec::new(), }; let pdf = crate::PdfDocument::new("HTML rendering demo") - .with_html(&input.html, &opts) + .with_html(&input.html, opts) .map_err(|e| PrintPdfApiReturn { pdf: String::new(), status: 2,