diff --git a/Cargo.lock b/Cargo.lock
index fe57108..6bde5f2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -160,21 +160,20 @@ dependencies = [
[[package]]
name = "azul-core"
version = "0.0.2"
-source = "git+https://github.com/fschutt/azul?rev=8b71bef5439b8f8d4542a2849e66499173562235#8b71bef5439b8f8d4542a2849e66499173562235"
+source = "git+https://github.com/fschutt/azul?rev=7e31b697b8e61779b88790aa9a74a95d8295a51a#7e31b697b8e61779b88790aa9a74a95d8295a51a"
dependencies = [
"azul-css",
"azul-css-parser",
"gl-context-loader",
"highway",
"libm",
- "rayon",
"rust-fontconfig",
]
[[package]]
name = "azul-css"
version = "0.0.1"
-source = "git+https://github.com/fschutt/azul?rev=8b71bef5439b8f8d4542a2849e66499173562235#8b71bef5439b8f8d4542a2849e66499173562235"
+source = "git+https://github.com/fschutt/azul?rev=7e31b697b8e61779b88790aa9a74a95d8295a51a#7e31b697b8e61779b88790aa9a74a95d8295a51a"
dependencies = [
"libm",
]
@@ -182,7 +181,7 @@ dependencies = [
[[package]]
name = "azul-css-parser"
version = "0.0.1"
-source = "git+https://github.com/fschutt/azul?rev=8b71bef5439b8f8d4542a2849e66499173562235#8b71bef5439b8f8d4542a2849e66499173562235"
+source = "git+https://github.com/fschutt/azul?rev=7e31b697b8e61779b88790aa9a74a95d8295a51a#7e31b697b8e61779b88790aa9a74a95d8295a51a"
dependencies = [
"azul-css",
"azul-simplecss",
@@ -191,7 +190,7 @@ dependencies = [
[[package]]
name = "azul-layout"
version = "0.0.4"
-source = "git+https://github.com/fschutt/azul?rev=8b71bef5439b8f8d4542a2849e66499173562235#8b71bef5439b8f8d4542a2849e66499173562235"
+source = "git+https://github.com/fschutt/azul?rev=7e31b697b8e61779b88790aa9a74a95d8295a51a#7e31b697b8e61779b88790aa9a74a95d8295a51a"
dependencies = [
"azul-core",
"azul-css",
@@ -209,7 +208,7 @@ checksum = "c303bfdf857413adbd19d9b15dddd9b4adb8e2be7e493c8a38d9fc6179a074ac"
[[package]]
name = "azul-text-layout"
version = "0.0.5"
-source = "git+https://github.com/fschutt/azul?rev=8b71bef5439b8f8d4542a2849e66499173562235#8b71bef5439b8f8d4542a2849e66499173562235"
+source = "git+https://github.com/fschutt/azul?rev=7e31b697b8e61779b88790aa9a74a95d8295a51a#7e31b697b8e61779b88790aa9a74a95d8295a51a"
dependencies = [
"allsorts",
"azul-core",
@@ -223,7 +222,7 @@ dependencies = [
[[package]]
name = "azulc"
version = "0.0.3"
-source = "git+https://github.com/fschutt/azul?rev=8b71bef5439b8f8d4542a2849e66499173562235#8b71bef5439b8f8d4542a2849e66499173562235"
+source = "git+https://github.com/fschutt/azul?rev=7e31b697b8e61779b88790aa9a74a95d8295a51a#7e31b697b8e61779b88790aa9a74a95d8295a51a"
dependencies = [
"azul-core",
"azul-css",
diff --git a/Cargo.toml b/Cargo.toml
index 7f6c8ba..8e9e2db 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -26,12 +26,12 @@ pdf-writer = { version = "0.12.0" }
image = { version = "0.25", default-features = false }
svg2pdf = { version = "0.12.0" }
# dependencies for wasm demo
-azul-css = { git = "https://github.com/fschutt/azul", rev = "8b71bef5439b8f8d4542a2849e66499173562235", default-features = false }
-azul-css-parser = { git = "https://github.com/fschutt/azul", rev = "8b71bef5439b8f8d4542a2849e66499173562235", default-features = false }
-azul-core = { git = "https://github.com/fschutt/azul", rev = "8b71bef5439b8f8d4542a2849e66499173562235", default-features = false, features = ["std"] }
-azul-layout = { git = "https://github.com/fschutt/azul", rev = "8b71bef5439b8f8d4542a2849e66499173562235", default-features = false, features = ["std", "text_layout"] }
-azul-text-layout = { git = "https://github.com/fschutt/azul", rev = "8b71bef5439b8f8d4542a2849e66499173562235", default-features = false }
-azulc = { git = "https://github.com/fschutt/azul", rev = "8b71bef5439b8f8d4542a2849e66499173562235", default-features = false, features = ["std", "xml", "text_layout", "font_loading"] }
+azul-css = { git = "https://github.com/fschutt/azul", rev = "7e31b697b8e61779b88790aa9a74a95d8295a51a", default-features = false }
+azul-css-parser = { git = "https://github.com/fschutt/azul", rev = "7e31b697b8e61779b88790aa9a74a95d8295a51a", default-features = false }
+azul-core = { git = "https://github.com/fschutt/azul", rev = "7e31b697b8e61779b88790aa9a74a95d8295a51a", default-features = false, features = ["std"] }
+azul-layout = { git = "https://github.com/fschutt/azul", rev = "7e31b697b8e61779b88790aa9a74a95d8295a51a", default-features = false, features = ["std", "text_layout"] }
+azul-text-layout = { git = "https://github.com/fschutt/azul", rev = "7e31b697b8e61779b88790aa9a74a95d8295a51a", default-features = false }
+azulc = { git = "https://github.com/fschutt/azul", rev = "7e31b697b8e61779b88790aa9a74a95d8295a51a", default-features = false, features = ["std", "xml", "text_layout", "font_loading"] }
rust-fontconfig = { version = "0.1.13", default-features = false }
xmlparser = { version = "0.13.6", default-features = false }
serde = { version = "1" }
diff --git a/examples/html.rs b/examples/html.rs
index e8d091e..e52cc8d 100644
--- a/examples/html.rs
+++ b/examples/html.rs
@@ -1,20 +1,14 @@
use printpdf::*;
-const HTML_STRINGS: &[&str; 2] = &[
- "
",
- "
-
Very long text that breaks into multiple lines. asdfasd asdfasdf adsfasdf ladsjfplasdjf asdlfkjasdfl lasdkjfasdölkjf
-
",
-];
+const HTML_STRINGS: &[&str; 1] = &[""];
-pub struct ImgComponent { }
+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())]
+ args: vec![("src".to_string(), "String".to_string())],
}
}
@@ -24,35 +18,55 @@ impl XmlComponentTrait for ImgComponent {
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()))
+ use printpdf::html::ImageInfo;
+
+ let im_info = arguments
+ .values
+ .get("src")
+ .map(|s| s.as_bytes().to_vec())
+ .unwrap_or_default();
+
+ let image_info = serde_json::from_slice::(&im_info)
+ .ok()
+ .unwrap_or_default();
+ let data_format = RawImageFormat::RGB8;
+
+ let image = RawImage {
+ width: image_info.width,
+ height: image_info.height,
+ data_format,
+ pixels: RawImageData::empty(data_format),
+ tag: im_info,
+ };
+
+ let im = Dom::image(image.to_internal()).style(CssApiWrapper::empty());
+
+ Ok(im)
}
}
fn main() -> Result<(), String> {
-
for (i, h) in HTML_STRINGS.iter().enumerate() {
-
let components = vec![XmlComponent {
id: "img".to_string(),
- renderer: Box::new(ImgComponent { }),
+ renderer: Box::new(ImgComponent {}),
inherit_vars: false,
}];
let config = XmlRenderOptions {
components,
- .. Default::default()
+ images: vec![(
+ "test.bmp".to_string(),
+ include_bytes!("./assets/img/BMP_test.bmp").to_vec(),
+ )]
+ .into_iter()
+ .collect(),
+ ..Default::default()
};
- let doc = PdfDocument::new("HTML rendering demo")
- .with_html(h, config)?
- .save(&PdfSaveOptions::default());
+ let mut doc = PdfDocument::new("HTML rendering demo");
+ let pages = doc.html2pages(h, config)?;
+ let doc = doc.with_pages(pages).save(&PdfSaveOptions::default());
std::fs::write(format!("html{i}.pdf"), doc).unwrap();
}
diff --git a/examples/subset.rs b/examples/subset.rs
index d564c28..c387aca 100644
--- a/examples/subset.rs
+++ b/examples/subset.rs
@@ -1,25 +1,18 @@
-use allsorts::subset;
use printpdf::*;
const WIN_1252: &[char; 214] = &[
- '!', '"','#', '$', '%','&', '\'',
- '(', ')', '*', '+', ',', '-', '.', '/',
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<',
- '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
- 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
- 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c',
- 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
- 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}',
- '~', '€', '‚', 'ƒ', '„', '…', '†', '‡', 'ˆ', '‰', 'Š', '‹', 'Œ',
- 'Ž', '‘', '’', '“', '•', '–', '—', '˜', '™', 'š', '›', 'œ', 'ž',
- 'Ÿ', '¡', '¢', '£', '¤', '¥', '¦', '§', '¨', '©', 'ª', '«', '¬',
- '®', '¯', '°', '±', '²', '³', '´', 'µ', '¶', '·', '¸', '¹', 'º',
- '»', '¼', '½', '¾', '¿', 'À', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Æ', 'Ç',
- 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï', 'Ð', 'Ñ', 'Ò', 'Ó', 'Ô',
- 'Õ', 'Ö', '×', 'Ø', 'Ù', 'Ú', 'Û', 'Ü', 'Ý', 'Þ', 'ß', 'à', 'á',
- 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î',
- 'ï', 'ð', 'ñ', 'ò', 'ó', 'ô', 'õ', 'ö', '÷', 'ø', 'ù', 'ú', 'û',
- 'ü', 'ý', 'þ', 'ÿ'
+ '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F',
+ 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',
+ 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
+ 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', '€',
+ '‚', 'ƒ', '„', '…', '†', '‡', 'ˆ', '‰', 'Š', '‹', 'Œ', 'Ž', '‘', '’', '“', '•', '–', '—', '˜',
+ '™', 'š', '›', 'œ', 'ž', 'Ÿ', '¡', '¢', '£', '¤', '¥', '¦', '§', '¨', '©', 'ª', '«', '¬', '®',
+ '¯', '°', '±', '²', '³', '´', 'µ', '¶', '·', '¸', '¹', 'º', '»', '¼', '½', '¾', '¿', 'À', 'Á',
+ 'Â', 'Ã', 'Ä', 'Å', 'Æ', 'Ç', 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï', 'Ð', 'Ñ', 'Ò', 'Ó', 'Ô',
+ 'Õ', 'Ö', '×', 'Ø', 'Ù', 'Ú', 'Û', 'Ü', 'Ý', 'Þ', 'ß', 'à', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç',
+ 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï', 'ð', 'ñ', 'ò', 'ó', 'ô', 'õ', 'ö', '÷', 'ø', 'ù', 'ú',
+ 'û', 'ü', 'ý', 'þ', 'ÿ',
];
const FONTS: &[(BuiltinFont, &[u8])] = &[
diff --git a/src/font.rs b/src/font.rs
index 6988a6f..dd663a6 100644
--- a/src/font.rs
+++ b/src/font.rs
@@ -320,10 +320,8 @@ impl ParsedFont {
}
pub fn subset_simple(&self, chars: &BTreeSet) -> Result {
-
let scope = ReadScope::new(&self.original_bytes);
- let font_file = scope.read::>()
- .map_err(|e| e.to_string())?;
+ let font_file = scope.read::>().map_err(|e| e.to_string())?;
let provider = font_file
.table_provider(self.original_index)
.map_err(|e| e.to_string())?;
@@ -344,9 +342,8 @@ impl ParsedFont {
let mut gids = p.iter().map(|s| s.0).collect::>();
gids.sort();
gids.dedup();
-
- let bytes = allsorts::subset::subset(&provider, &gids)
- .map_err(|e| e.to_string())?;
+
+ let bytes = allsorts::subset::subset(&provider, &gids).map_err(|e| e.to_string())?;
Ok(SubsetFont {
bytes,
@@ -372,9 +369,11 @@ impl ParsedFont {
.table_provider(self.original_index)
.map_err(|e| e.to_string())?;
- let font =
- allsorts::subset::subset(&provider, &glyph_ids.iter().map(|s| s.0).collect::>())
- .map_err(|e| e.to_string())?;
+ let font = allsorts::subset::subset(
+ &provider,
+ &glyph_ids.iter().map(|s| s.0).collect::>(),
+ )
+ .map_err(|e| e.to_string())?;
Ok(SubsetFont {
bytes: font,
@@ -633,7 +632,7 @@ impl ParsedFont {
let second_font_file = second_scope.read::>().ok()?;
let second_provider = second_font_file.table_provider(font_index).ok()?;
- let mut font_data_impl = allsorts::font::Font::new(second_provider).ok()?;
+ let font_data_impl = allsorts::font::Font::new(second_provider).ok()?;
// required for font layout: gsub_cache, gpos_cache and gdef_table
let gsub_cache = None; // font_data_impl.gsub_cache().ok().and_then(|s| s);
@@ -651,7 +650,6 @@ impl ParsedFont {
println!("warning: no cmap subtable");
}
-
let hmtx_data = provider
.table_data(tag::HMTX)
.ok()
@@ -668,7 +666,19 @@ impl ParsedFont {
.table_data(tag::HHEA)
.ok()
.and_then(|hhea_data| ReadScope::new(&hhea_data?).read::().ok())
- .unwrap_or(unsafe { std::mem::zeroed() });
+ .unwrap_or(HheaTable {
+ ascender: 0,
+ descender: 0,
+ line_gap: 0,
+ advance_width_max: 0,
+ min_left_side_bearing: 0,
+ min_right_side_bearing: 0,
+ x_max_extent: 0,
+ caret_slope_rise: 0,
+ caret_slope_run: 0,
+ caret_offset: 0,
+ num_h_metrics: 0,
+ });
let font_metrics = FontMetrics::from_bytes(font_bytes, font_index);
diff --git a/src/html.rs b/src/html.rs
index c68114f..db3fe16 100644
--- a/src/html.rs
+++ b/src/html.rs
@@ -1,8 +1,13 @@
use crate::{BuiltinFont, Mm, Op, PdfDocument, PdfPage, PdfResources, Pt};
+pub use azul_core::dom::Dom;
+pub use azul_core::styled_dom::StyledDom;
+pub use azul_core::xml::{
+ CompileError, ComponentArguments, FilteredComponentArguments, RenderDomError, XmlComponent,
+ XmlComponentMap, XmlComponentTrait, XmlNode, XmlTextContent,
+};
use azul_core::{
app_resources::{
- DpiScaleFactor, Epoch, IdNamespace, ImageCache,
- ImageDescriptor, ImageRef, ImageRefHash, RendererResources
+ DecodedImage, DpiScaleFactor, Epoch, IdNamespace, ImageCache, ImageRef, RendererResources,
},
callbacks::DocumentId,
display_list::{
@@ -10,29 +15,16 @@ use azul_core::{
StyleBorderStyles, StyleBorderWidths,
},
dom::{NodeData, NodeId},
- styled_dom::{ContentGroup, DomId, StyledNode},
+ styled_dom::{ContentGroup, StyledNode},
ui_solver::LayoutResult,
- window::{FullWindowState, LogicalSize}, xml::Xml,
+ window::{FullWindowState, LogicalSize},
};
-use azul_css::{CssPropertyValue, FloatValue, LayoutDisplay, StyleTextColor, U8Vec};
-use base64::Engine;
+use azul_css::{CssPropertyValue, FloatValue, LayoutDisplay, StyleTextColor};
+pub use azul_css_parser::CssApiWrapper;
use rust_fontconfig::{FcFont, FcFontCache, FcPattern};
+use serde_derive::{Deserialize, Serialize};
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;
+use svg2pdf::usvg::tiny_skia_path::Scalar;
const DPI_SCALE: DpiScaleFactor = DpiScaleFactor {
inner: FloatValue::const_new(1),
@@ -44,12 +36,10 @@ const DOCUMENT_ID: DocumentId = DocumentId {
id: 0,
};
-pub type Base64String = String;
-
#[derive(Debug)]
pub struct XmlRenderOptions {
- pub images: BTreeMap,
- pub fonts: BTreeMap,
+ pub images: BTreeMap>,
+ pub fonts: BTreeMap>,
pub page_width: Mm,
pub page_height: Mm,
pub components: Vec,
@@ -72,14 +62,15 @@ pub(crate) fn xml_to_pages(
config: XmlRenderOptions,
document: &mut PdfDocument,
) -> Result, String> {
-
let size = LogicalSize {
width: config.page_width.into_pt().0,
height: config.page_height.into_pt().0,
};
- let root_nodes = azulc_lib::xml::parse_xml_string(&fixup_xml(file_contents))
- .map_err(|e| format!("Error parsing XML: {}", e))?;
+ // inserts images into the PDF resources and changes the src="..."
+ let xml = fixup_xml(file_contents, document, &config);
+ let root_nodes =
+ azulc_lib::xml::parse_xml_string(&xml).map_err(|e| format!("Error parsing XML: {}", e))?;
let fixup = fixup_xml_nodes(&root_nodes);
@@ -89,21 +80,22 @@ pub(crate) fn xml_to_pages(
}
let styled_dom = azul_core::xml::str_to_dom(
- fixup.as_ref(),
- &mut components,
- Some(config.page_width.into_pt().0)
- ).map_err(|e| format!("Error constructing DOM: {}", e.to_string()))?;
+ fixup.as_ref(),
+ &mut components,
+ Some(config.page_width.into_pt().0),
+ )
+ .map_err(|e| format!("Error constructing DOM: {}", e.to_string()))?;
- let dom_id = DomId { inner: 0 };
let mut fake_window_state = FullWindowState::default();
fake_window_state.size.dimensions = size;
let mut renderer_resources = RendererResources::default();
+
let image_cache = ImageCache {
image_id_map: config
.images
.iter()
.filter_map(|(id, bytes)| {
- let bytes = base64::prelude::BASE64_STANDARD.decode(bytes).ok()?;
+ // let bytes = base64::prelude::BASE64_STANDARD.decode(bytes).ok()?;
let decoded = crate::image::RawImage::decode_from_bytes(&bytes).ok()?;
let raw_image = crate::image::translate_to_internal_rawimage(&decoded);
Some((id.clone().into(), ImageRef::new_rawimage(raw_image)?))
@@ -138,14 +130,14 @@ pub(crate) fn xml_to_pages(
&config
.fonts
.iter()
- .filter_map(|(id, font_base64)| {
- let bytes = base64::prelude::BASE64_STANDARD.decode(font_base64).ok()?;
+ .filter_map(|(id, bytes)| {
+ // let bytes = base64::prelude::BASE64_STANDARD.decode(font_base64).ok()?;
let pat = FcPattern {
name: Some(id.split(".").next().unwrap_or("").to_string()),
..Default::default()
};
let font = FcFont {
- bytes,
+ bytes: bytes.clone(),
font_index: 0,
};
Some((pat, font))
@@ -243,7 +235,28 @@ fn get_fcpat(b: BuiltinFont) -> (FcPattern, FcFont) {
)
}
-fn fixup_xml(s: &str) -> String {
+#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
+pub struct ImageInfo {
+ pub original_id: String,
+ pub xobject_id: String,
+ pub image_type: ImageTypeInfo,
+ pub width: usize,
+ pub height: usize,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
+pub enum ImageTypeInfo {
+ Image,
+ Svg,
+}
+
+impl Default for ImageTypeInfo {
+ fn default() -> Self {
+ ImageTypeInfo::Image
+ }
+}
+
+fn fixup_xml(s: &str, doc: &mut PdfDocument, config: &XmlRenderOptions) -> String {
let s = if !s.contains("") {
format!("{s}")
} else {
@@ -254,7 +267,60 @@ fn fixup_xml(s: &str) -> String {
} else {
s.trim().to_string()
};
- s.trim().to_string()
+
+ let mut s = s.trim().to_string();
+
+ for (k, image_bytes) in config.images.iter() {
+ let opt_svg = std::str::from_utf8(&image_bytes)
+ .ok()
+ .and_then(|s| crate::Svg::parse(s).ok());
+
+ let img_info = match opt_svg {
+ Some(o) => {
+ let width = o.width.map(|s| s.0).unwrap_or(0);
+ let height = o.height.map(|s| s.0).unwrap_or(0);
+ let image_xobject_id = doc.add_xobject(&o);
+ ImageInfo {
+ original_id: k.clone(),
+ xobject_id: image_xobject_id.0,
+ image_type: ImageTypeInfo::Svg,
+ width,
+ height,
+ }
+ }
+ None => {
+ let raw_image = match crate::image::RawImage::decode_from_bytes(&image_bytes) {
+ Ok(o) => o,
+ Err(e) => {
+ #[cfg(not(target_family = "wasm"))]
+ {
+ println!("{e}");
+ }
+ continue;
+ }
+ };
+
+ let width = raw_image.width;
+ let height = raw_image.height;
+ let image_xobject_id = doc.add_image(&raw_image);
+ ImageInfo {
+ original_id: k.clone(),
+ xobject_id: image_xobject_id.0,
+ image_type: ImageTypeInfo::Image,
+ width,
+ height,
+ }
+ }
+ };
+
+ let json = serde_json::to_string(&img_info).unwrap_or_default();
+
+ s = s
+ .replace(&format!("src='{k}'"), &format!("src='{json}'"))
+ .replace(&format!("src=\"{k}\""), &format!("src='{json}'"));
+ }
+
+ s
}
fn fixup_xml_nodes(nodes: &[XmlNode]) -> Vec {
@@ -460,18 +526,34 @@ fn displaylist_handle_rect(
});
}
- if let Some((desc, data, hash, size)) = opt_image {
- let image_id = format!("im_azul_layout_{h:032}", h = hash.0);
- let xobject_id = crate::XObjectId(image_id.clone());
- let image = crate::XObject::Image(crate::image::translate_from_internal_rawimage(
- desc,
- data.as_slice(),
- ));
- doc.resources.xobjects.map.insert(xobject_id, image);
+ if let Some(image_info) = opt_image {
+ let source_width = image_info.width;
+ let source_height = image_info.width;
+ let target_width = positioned_rect.size.width;
+ let target_height = positioned_rect.size.height;
+ let pos = positioned_rect.position.get_static_offset();
+
+ let is_zero = target_width.is_nearly_zero()
+ || target_height.is_nearly_zero()
+ || source_height == 0
+ || source_width == 0;
+
+ if !is_zero {
+ ops.push(Op::UseXObject {
+ id: crate::XObjectId(image_info.xobject_id.clone()),
+ transform: crate::XObjectTransform {
+ translate_x: Some(Pt(pos.x)),
+ translate_y: Some(Pt(page_height.0 - pos.y)),
+ rotate: None, // todo
+ scale_x: Some(target_width / source_width as f32),
+ scale_y: Some(target_height / source_height as f32),
+ dpi: None,
+ },
+ });
+ }
}
if let Some((text, id, color, space_index)) = opt_text {
-
ops.push(Op::StartTextSection);
ops.push(Op::SetFillColor {
col: crate::Color::Rgb(crate::Rgb {
@@ -485,22 +567,29 @@ fn displaylist_handle_rect(
mode: crate::TextRenderingMode::Fill,
});
ops.push(Op::SetWordSpacing { percent: 100.0 });
- ops.push(Op::SetLineHeight { lh: Pt(text.font_size_px) });
-
+ ops.push(Op::SetLineHeight {
+ lh: Pt(text.font_size_px),
+ });
+
let glyphs = text.get_layouted_glyphs();
let static_bounds = positioned_rect.get_approximate_static_bounds();
for gi in glyphs.glyphs {
- ops.push(Op::SetTextCursor { pos: crate::Point { x: Pt(0.0), y: Pt(0.0) } });
- ops.push(Op::SetTextMatrix {
+ ops.push(Op::SetTextCursor {
+ pos: crate::Point {
+ x: Pt(0.0),
+ y: Pt(0.0),
+ },
+ });
+ ops.push(Op::SetTextMatrix {
matrix: crate::TextMatrix::Translate(
- Pt(static_bounds.min_x() as f32 + (gi.point.x * 2.0)),
- Pt(page_height.0 - static_bounds.min_y() as f32 - gi.point.y),
- )
+ Pt(static_bounds.min_x() as f32 + (gi.point.x * 2.0)),
+ Pt(page_height.0 - static_bounds.min_y() as f32 - gi.point.y),
+ ),
});
ops.push(Op::WriteCodepoints {
- font: id.clone(),
+ font: id.clone(),
size: Pt(text.font_size_px * 2.0),
cp: vec![(gi.index as u16, ' ')],
});
@@ -510,6 +599,7 @@ fn displaylist_handle_rect(
}
if !newops.is_empty() {
+ println!("{newops:?}");
ops.push(Op::SaveGraphicsState);
ops.append(&mut newops);
ops.push(Op::RestoreGraphicsState);
@@ -683,27 +773,19 @@ fn get_background_content(
v
}
-fn get_image_node(
- html_node: &NodeData,
-) -> Option<(&ImageDescriptor, &U8Vec, ImageRefHash, LogicalSize)> {
- use azul_core::app_resources::DecodedImage;
- use azul_core::app_resources::ImageData;
+fn get_image_node(html_node: &NodeData) -> Option {
use azul_core::dom::NodeType;
- let image_ref = match html_node.get_node_type() {
- NodeType::Image(image_ref) => image_ref,
+ let data = match html_node.get_node_type() {
+ NodeType::Image(image_ref) => image_ref.get_data(),
_ => return None,
};
- let image_hash = image_ref.get_hash();
- let image_size = image_ref.get_size();
-
- let (descriptor, data) = match image_ref.get_data() {
- DecodedImage::Raw((descriptor, ImageData::Raw(vec))) => (descriptor, vec),
- _ => return None,
- };
-
- Some((descriptor, data, image_hash, image_size))
+ if let DecodedImage::NullImage { tag, .. } = data {
+ serde_json::from_slice(tag).ok()
+ } else {
+ None
+ }
}
fn get_text_node(
diff --git a/src/image.rs b/src/image.rs
index a5ba1f2..27b93dc 100644
--- a/src/image.rs
+++ b/src/image.rs
@@ -10,6 +10,7 @@ pub struct RawImage {
pub width: usize,
pub height: usize,
pub data_format: RawImageFormat,
+ pub tag: Vec,
}
struct RawImageU8 {
@@ -59,6 +60,41 @@ impl RawImageFormat {
}
}
+ fn from_internal(f: &azul_core::app_resources::RawImageFormat) -> Self {
+ use azul_core::app_resources::RawImageFormat;
+ match f {
+ RawImageFormat::R8 => crate::RawImageFormat::R8,
+ RawImageFormat::RG8 => crate::RawImageFormat::RG8,
+ RawImageFormat::RGB8 => crate::RawImageFormat::RGB8,
+ RawImageFormat::RGBA8 => crate::RawImageFormat::RGBA8,
+ RawImageFormat::R16 => crate::RawImageFormat::R16,
+ RawImageFormat::RG16 => crate::RawImageFormat::RG16,
+ RawImageFormat::RGB16 => crate::RawImageFormat::RGB16,
+ RawImageFormat::RGBA16 => crate::RawImageFormat::RGBA16,
+ RawImageFormat::BGR8 => crate::RawImageFormat::BGR8,
+ RawImageFormat::BGRA8 => crate::RawImageFormat::BGRA8,
+ RawImageFormat::RGBF32 => crate::RawImageFormat::RGBF32,
+ RawImageFormat::RGBAF32 => crate::RawImageFormat::RGBAF32,
+ }
+ }
+
+ fn into_internal(&self) -> azul_core::app_resources::RawImageFormat {
+ match self {
+ RawImageFormat::R8 => azul_core::app_resources::RawImageFormat::R8,
+ RawImageFormat::RG8 => azul_core::app_resources::RawImageFormat::RG8,
+ RawImageFormat::RGB8 => azul_core::app_resources::RawImageFormat::RGB8,
+ RawImageFormat::RGBA8 => azul_core::app_resources::RawImageFormat::RGBA8,
+ RawImageFormat::R16 => azul_core::app_resources::RawImageFormat::R16,
+ RawImageFormat::RG16 => azul_core::app_resources::RawImageFormat::RG16,
+ RawImageFormat::RGB16 => azul_core::app_resources::RawImageFormat::RGB16,
+ RawImageFormat::RGBA16 => azul_core::app_resources::RawImageFormat::RGBA16,
+ RawImageFormat::BGR8 => azul_core::app_resources::RawImageFormat::BGR8,
+ RawImageFormat::BGRA8 => azul_core::app_resources::RawImageFormat::BGRA8,
+ RawImageFormat::RGBF32 => azul_core::app_resources::RawImageFormat::RGBF32,
+ RawImageFormat::RGBAF32 => azul_core::app_resources::RawImageFormat::RGBAF32,
+ }
+ }
+
pub fn has_alpha(&self) -> bool {
use self::RawImageFormat::*;
matches!(self, RGBA8 | RGBA16 | RGBAF32)
@@ -94,7 +130,39 @@ pub enum RawImageData {
F32(Vec),
}
+impl RawImageData {
+ pub fn empty(format: RawImageFormat) -> Self {
+ use self::RawImageFormat::*;
+ match format {
+ R8 | RG8 | RGB8 | RGBA8 | BGR8 | BGRA8 => Self::U8(Vec::new()),
+
+ R16 | RG16 | RGB16 | RGBA16 => Self::U16(Vec::new()),
+
+ RGBF32 | RGBAF32 => Self::F32(Vec::new()),
+ }
+ }
+
+ pub fn is_empty(&self) -> bool {
+ match self {
+ RawImageData::U8(vec) => vec.is_empty(),
+ RawImageData::U16(vec) => vec.is_empty(),
+ RawImageData::F32(vec) => vec.is_empty(),
+ }
+ }
+}
+
impl RawImage {
+ /// Creates an empty `RawImage`
+ pub fn empty(width: usize, height: usize, format: crate::RawImageFormat) -> Self {
+ Self {
+ width,
+ height,
+ data_format: format,
+ pixels: RawImageData::empty(format),
+ tag: Vec::new(),
+ }
+ }
+
/// NOTE: depends on the enabled image formats!
pub fn decode_from_bytes(bytes: &[u8]) -> Result {
use image::DynamicImage::*;
@@ -102,64 +170,100 @@ impl RawImage {
let im = image::guess_format(bytes).map_err(|e| e.to_string())?;
let b_len = bytes.len();
- #[cfg(not(feature = "gif"))] {
+ #[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); }
+ if im == image::ImageFormat::Gif {
+ return Err(err);
+ }
}
- #[cfg(not(feature = "jpeg"))] {
+ #[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); }
+ if im == image::ImageFormat::Gif {
+ return Err(err);
+ }
}
- #[cfg(not(feature = "png"))] {
+ #[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); }
+ if im == image::ImageFormat::Png {
+ return Err(err);
+ }
}
- #[cfg(not(feature = "pnm"))] {
+ #[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); }
+ if im == image::ImageFormat::Pnm {
+ return Err(err);
+ }
}
- #[cfg(not(feature = "tiff"))] {
+ #[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); }
+ if im == image::ImageFormat::Tiff {
+ return Err(err);
+ }
}
-
- #[cfg(not(feature = "tiff"))] {
+
+ #[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); }
+ if im == image::ImageFormat::Tiff {
+ return Err(err);
+ }
}
- #[cfg(not(feature = "bmp"))] {
+ #[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); }
+ if im == image::ImageFormat::Bmp {
+ return Err(err);
+ }
}
- #[cfg(not(feature = "ico"))] {
+ #[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); }
+ if im == image::ImageFormat::Ico {
+ return Err(err);
+ }
}
- #[cfg(not(feature = "tga"))] {
+ #[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); }
+ if im == image::ImageFormat::Tga {
+ return Err(err);
+ }
}
- #[cfg(not(feature = "hdr"))] {
+ #[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); }
+ if im == image::ImageFormat::Hdr {
+ return Err(err);
+ }
}
- #[cfg(not(feature = "dds"))] {
+ #[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); }
+ if im == image::ImageFormat::Dds {
+ return Err(err);
+ }
}
- #[cfg(not(feature = "webp"))] {
+ #[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); }
+ if im == image::ImageFormat::WebP {
+ return Err(err);
+ }
}
let im = image::ImageReader::new(Cursor::new(bytes))
@@ -202,8 +306,26 @@ impl RawImage {
width: w as usize,
height: h as usize,
data_format: ct,
+ tag: Vec::new(),
})
}
+
+ /// Translates to an internal `RawImage`, necessary for the `` component
+ pub fn to_internal(&self) -> azul_core::app_resources::ImageRef {
+ let invalid = azul_core::app_resources::ImageRef::null_image(
+ self.width,
+ self.height,
+ self.data_format.into_internal(),
+ self.tag.clone(),
+ );
+
+ if self.pixels.is_empty() {
+ invalid
+ } else {
+ azul_core::app_resources::ImageRef::new_rawimage(translate_to_internal_rawimage(self))
+ .unwrap_or(invalid)
+ }
+ }
}
pub(crate) fn image_to_stream(im: RawImage, doc: &mut lopdf::Document) -> lopdf::Stream {
@@ -305,33 +427,6 @@ fn split_rawimage_into_rgb_plus_alpha(im: RawImage) -> (RawImageU8, Option RawImage {
- use azul_core::app_resources::RawImageFormat;
-
- RawImage {
- pixels: crate::RawImageData::U8(data.to_vec()),
- width: im.width,
- height: im.height,
- data_format: match im.format {
- RawImageFormat::R8 => crate::RawImageFormat::R8,
- RawImageFormat::RG8 => crate::RawImageFormat::RG8,
- RawImageFormat::RGB8 => crate::RawImageFormat::RGB8,
- RawImageFormat::RGBA8 => crate::RawImageFormat::RGBA8,
- RawImageFormat::R16 => crate::RawImageFormat::R16,
- RawImageFormat::RG16 => crate::RawImageFormat::RG16,
- RawImageFormat::RGB16 => crate::RawImageFormat::RGB16,
- RawImageFormat::RGBA16 => crate::RawImageFormat::RGBA16,
- RawImageFormat::BGR8 => crate::RawImageFormat::BGR8,
- RawImageFormat::BGRA8 => crate::RawImageFormat::BGRA8,
- RawImageFormat::RGBF32 => crate::RawImageFormat::RGBF32,
- RawImageFormat::RGBAF32 => crate::RawImageFormat::RGBAF32,
- },
- }
-}
-
pub fn translate_to_internal_rawimage(im: &RawImage) -> azul_core::app_resources::RawImage {
azul_core::app_resources::RawImage {
pixels: match &im.pixels {
@@ -346,19 +441,7 @@ pub fn translate_to_internal_rawimage(im: &RawImage) -> azul_core::app_resources
width: im.width,
height: im.height,
premultiplied_alpha: false,
- data_format: match &im.data_format {
- RawImageFormat::R8 => azul_core::app_resources::RawImageFormat::R8,
- RawImageFormat::RG8 => azul_core::app_resources::RawImageFormat::RG8,
- RawImageFormat::RGB8 => azul_core::app_resources::RawImageFormat::RGB8,
- RawImageFormat::RGBA8 => azul_core::app_resources::RawImageFormat::RGBA8,
- RawImageFormat::R16 => azul_core::app_resources::RawImageFormat::R16,
- RawImageFormat::RG16 => azul_core::app_resources::RawImageFormat::RG16,
- RawImageFormat::RGB16 => azul_core::app_resources::RawImageFormat::RGB16,
- RawImageFormat::RGBA16 => azul_core::app_resources::RawImageFormat::RGBA16,
- RawImageFormat::BGR8 => azul_core::app_resources::RawImageFormat::BGR8,
- RawImageFormat::BGRA8 => azul_core::app_resources::RawImageFormat::BGRA8,
- RawImageFormat::RGBF32 => azul_core::app_resources::RawImageFormat::RGBF32,
- RawImageFormat::RGBAF32 => azul_core::app_resources::RawImageFormat::RGBAF32,
- },
+ data_format: im.data_format.into_internal(),
+ tag: im.tag.clone().into(),
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 485e8e8..f62e174 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -194,15 +194,13 @@ impl PdfDocument {
id
}
- /// Returns the `Pages` rendered by the HTML, adds resources automatically
- pub fn with_html(
+ /// Renders HTML to pages
+ pub fn html2pages(
&mut self,
html: &str,
config: XmlRenderOptions,
- ) -> Result<&mut Self, String> {
- let mut pages = crate::html::xml_to_pages(html, config, self)?;
- self.pages.append(&mut pages);
- Ok(self)
+ ) -> Result, String> {
+ crate::html::xml_to_pages(html, config, self)
}
/// Replaces `document.pages` with the new pages
diff --git a/src/serialize.rs b/src/serialize.rs
index 8d1d45d..ba48a0c 100644
--- a/src/serialize.rs
+++ b/src/serialize.rs
@@ -49,6 +49,7 @@ impl Default for PdfSaveOptions {
pub fn serialize_pdf_into_bytes(pdf: &PdfDocument, opts: &PdfSaveOptions) -> Vec {
let mut doc = lopdf::Document::with_version("1.3");
+ doc.reference_table.cross_reference_type = lopdf::xref::XrefType::CrossReferenceTable;
let pages_id = doc.new_object_id();
let mut catalog = LoDictionary::from_iter(vec![
("Type", "Catalog".into()),
@@ -889,13 +890,14 @@ fn prepare_fonts(resources: &PdfResources, pages: &[PdfPage]) -> BTreeMap>()) {
- Ok(o) => o,
- Err(e) => {
- println!("{e}");
- continue;
- }
- };
+ let subset_font =
+ match font.subset(&glyph_ids.iter().map(|s| (*s.0, *s.1)).collect::>()) {
+ Ok(o) => o,
+ Err(e) => {
+ println!("{e}");
+ continue;
+ }
+ };
let font = match ParsedFont::from_bytes(&subset_font.bytes, 0) {
Some(s) => s,
None => continue,
diff --git a/src/svg.rs b/src/svg.rs
index 078a156..bee5f5e 100644
--- a/src/svg.rs
+++ b/src/svg.rs
@@ -46,7 +46,8 @@ impl Svg {
// Let's first convert the SVG into an independent chunk.
let mut options = usvg::Options::default();
- #[cfg(not(target_arch = "wasm32"))] {
+ #[cfg(not(target_arch = "wasm32"))]
+ {
options.fontdb_mut().load_system_fonts();
}
let tree = usvg::Tree::from_str(svg_string, &options)
diff --git a/src/utils.rs b/src/utils.rs
index ad6244f..31de1bd 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -1,5 +1,3 @@
-use base64::Engine;
-
use crate::date::OffsetDateTime;
use std::io::Read;
use std::sync::atomic::{AtomicUsize, Ordering};
diff --git a/src/wasm.rs b/src/wasm.rs
index 1ed7a0c..ab9a5ef 100644
--- a/src/wasm.rs
+++ b/src/wasm.rs
@@ -4,14 +4,16 @@ use std::collections::BTreeMap;
use crate::{serialize::PdfSaveOptions, XmlRenderOptions};
+pub type Base64String = String;
+
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct PrintPdfApiInput {
#[serde(default, skip_serializing_if = "String::is_empty")]
pub html: String,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
- pub images: BTreeMap>,
+ pub images: BTreeMap,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
- pub fonts: BTreeMap>,
+ pub fonts: BTreeMap,
#[serde(default, skip_serializing_if = "PdfGenerationOptions::is_default")]
pub options: PdfGenerationOptions,
}
@@ -73,19 +75,34 @@ fn printpdf_from_xml_internal(
let opts = XmlRenderOptions {
page_width: Mm(input.options.page_width_mm.unwrap_or(210.0)),
page_height: Mm(input.options.page_height_mm.unwrap_or(297.0)),
- images: BTreeMap::new(),
- fonts: BTreeMap::new(),
+ images: input
+ .images
+ .iter()
+ .filter_map(|(k, v)| {
+ Some((k.clone(), base64::prelude::BASE64_STANDARD.decode(v).ok()?))
+ })
+ .collect(),
+ fonts: input
+ .fonts
+ .iter()
+ .filter_map(|(k, v)| {
+ Some((k.clone(), base64::prelude::BASE64_STANDARD.decode(v).ok()?))
+ })
+ .collect(),
components: Vec::new(),
};
- let pdf = crate::PdfDocument::new("HTML rendering demo")
- .with_html(&input.html, opts)
+ let mut pdf = crate::PdfDocument::new("HTML rendering demo");
+
+ let pages = pdf
+ .html2pages(&input.html, opts)
.map_err(|e| PrintPdfApiReturn {
pdf: String::new(),
status: 2,
error: e,
- })?
- .save(&PdfSaveOptions::default());
+ })?;
+
+ let pdf = pdf.with_pages(pages).save(&PdfSaveOptions::default());
Ok(PrintPdfApiReturn {
pdf: BASE64_STANDARD.encode(pdf),