1#![warn(missing_docs)]
168#![warn(clippy::missing_errors_doc)]
169
170mod catalog;
171#[cfg(test)]
172mod cms_failure_contract;
173#[cfg(test)]
174#[path = "../tests/common/codec_fixtures.rs"]
175mod codec_fixtures;
176mod color;
177mod compatibility;
178mod css_syntax;
179mod diagnostic_registry;
180mod font_metadata;
181mod fonts;
182mod frame;
183#[cfg(test)]
184#[path = "../tests/common/fuzz_seed.rs"]
185mod fuzz_seed;
186#[cfg(test)]
187mod metadata_failure_contract;
188mod normalization;
189mod outcome;
190#[cfg(test)]
191#[allow(dead_code)]
192#[path = "../tests/common/png_color_reference.rs"]
193mod png_color_reference;
194#[cfg(test)]
195#[allow(dead_code)]
196#[path = "../tests/common/png_icc_safety.rs"]
197mod png_icc_safety;
198#[cfg(test)]
199mod png_projection_contract;
200#[cfg(test)]
201#[allow(dead_code)]
202#[path = "../tests/common/png_stage4.rs"]
203mod png_stage4;
204#[cfg(all(test, target_arch = "wasm32"))]
205#[path = "../tests/common/png_stage4_composition.rs"]
206mod png_stage4_composition;
207#[cfg(test)]
208#[allow(dead_code)]
209#[path = "../tests/common/png_stage4_reference.rs"]
210mod png_stage4_reference;
211#[cfg(test)]
212#[allow(dead_code)]
213#[path = "../tests/common/png_stage4_review.rs"]
214mod png_stage4_review;
215mod portable_math;
216mod relationships;
217mod render;
218mod request;
219mod resource_accounting;
220mod resource_limits;
221mod scene;
222#[cfg(test)]
223mod scene_fuzz;
224mod scene_surface;
225mod shaping;
226#[cfg(test)]
227mod stage4_diagnostic_contract;
228
229#[cfg(doctest)]
232#[doc = include_str!("../README.md")]
233struct ReadmeDoctests;
234
235pub use compatibility::{
236 AssetChainEntry, DiagnosticDetails, ErrorCode, ErrorDescriptor, ErrorFamily, FiniteValue,
237 LogicalBounds, Measurement, MetadataComponent, MetadataOperation, NestedPath,
238 NestedPathSegment, NestedSegmentKind, Recovery, Renderer, ResourceMeasurement, ResourceValue,
239 SvgFontSubsetStage, ValueKind,
240};
241#[doc(hidden)]
242pub use compatibility::{CompatibilityRevision, ReferenceProfileIdentity};
243pub use frame::{
244 FrameBatchError, FrameBatchErrorKind, FrameKind, FrameRef, FrameRefs, FrameRenderCursor,
245 FrameRenderEntry,
246};
247pub use outcome::{
248 Artifact, CompatibilityDiagnostic, CompatibilityReport, DiagnosticCode, DiagnosticEffect,
249 DiagnosticSeverity, RenderError, RenderErrorCategory, RenderOutcome,
250};
251pub use request::{
252 ColorMode, FrameOptions, FrameRenderRequest, FrameRenderRequestBuilder, MetadataOptions,
253 OutputFormat, PdfOptions, PdfRasterFallback, RenderRequest, RenderRequestBuilder, RequestError,
254 Sizing, SupplementalAsset, SupplementalAssets, SupplementalAssetsBuilder,
255};
256pub use resource_limits::{ResourceLimits, ResourceLimitsBuilder};
257pub use scene::{SceneDocument, SceneParseError};
258
259#[cfg(all(test, target_arch = "wasm32"))]
260pub mod wasm_tests {
261 use wasm_bindgen_test::wasm_bindgen_test;
262
263 wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_dedicated_worker);
264
265 #[wasm_bindgen_test]
266 fn png_sdr_color_reference_in_wasm() {
267 png_color_cases_in_wasm(crate::png_color_reference::evidence());
268 }
269
270 #[wasm_bindgen_test]
271 fn png_gain_map_reference_in_wasm() {
272 png_color_cases_in_wasm(crate::png_color_reference::gain_map_evidence());
273 }
274
275 #[wasm_bindgen_test]
276 fn png_stage4_icc_reference_in_wasm() {
277 png_color_cases_in_wasm(crate::png_stage4_reference::evidence());
278 }
279 #[wasm_bindgen_test]
280 fn cms_allocation_failures_in_wasm() {
281 crate::cms_failure_contract::cms_out_of_memory_is_fatal_at_every_operation_boundary();
282 }
283 #[wasm_bindgen_test]
284 fn png_stage4_profile_composition_in_wasm() {
285 png_color_cases_in_wasm(crate::png_stage4_composition::evidence());
286 }
287 #[wasm_bindgen_test]
288 fn png_stage4_review_regressions_in_wasm() {
289 png_stage4_bytes(crate::png_icc_safety::headers());
290 png_color_cases_in_wasm(crate::png_icc_safety::sampled_evidence());
291 png_stage4_bytes(crate::png_stage4_review::adam7());
292 png_stage4_bytes(crate::png_stage4_review::icc_extents());
293 png_stage4_bytes(crate::png_stage4_review::lower_keys());
294 png_color_cases_in_wasm(crate::png_stage4_review::gray_lut_equivalence());
295 }
296 #[wasm_bindgen_test]
297 fn png_profile_projection_limits_in_wasm() {
298 crate::png_projection_contract::profile_limits(Some(0));
299 }
300 #[wasm_bindgen_test]
301 fn png_profile_rounded_limits_in_wasm() {
302 crate::png_projection_contract::profile_limits(Some(1));
303 }
304 #[wasm_bindgen_test]
305 fn png_profile_frame_mask_limits_in_wasm() {
306 crate::png_projection_contract::profile_limits(Some(2));
307 }
308
309 #[wasm_bindgen_test]
310 fn png_stage4_byte_contracts_in_wasm() {
311 use crate::png_stage4 as v;
312 for rows in [
313 v::orientations(),
314 v::invalid_orientation(),
315 v::chunk_cases(),
316 v::scanline_cases(),
317 v::palette_cases(),
318 v::apng_controls(),
319 v::apng_malformed(),
320 v::invalid_cicp(),
321 v::large_streams(),
322 v::composition(&[(*b"gAMA", 100000_u32.to_be_bytes().to_vec())], true),
323 v::hdr_composition(16),
324 v::hdr_composition(18),
325 ] {
326 png_stage4_bytes(rows);
327 }
328 }
329
330 #[wasm_bindgen_test]
331 fn png_stage4_ihdr_contracts_in_wasm() {
332 png_stage4_bytes(crate::png_stage4::ihdr_cases());
333 }
334
335 fn png_stage4_bytes(rows: Vec<crate::png_stage4::Case>) {
336 use crate::png_stage4::{self as v, Outcome};
337 for row in rows {
338 let source = v::scene(&row);
339 let scene = crate::SceneDocument::parse(&serde_json::to_vec(&source).unwrap()).unwrap();
340 let request = crate::RenderRequest::builder(crate::OutputFormat::Png)
341 .padding(0.0)
342 .include_background(false)
343 .build()
344 .unwrap();
345 let first = crate::Renderer::new().render(&scene, &request).unwrap();
346 let second = crate::Renderer::new().render(&scene, &request).unwrap();
347 assert_eq!(first.artifact().bytes(), second.artifact().bytes());
348 assert_eq!(first.report(), second.report());
349 v::report(&row, "png", serde_json::to_value(first.report()).unwrap());
350 if matches!(row.outcome, Outcome::Accepted(_)) {
351 let actual = crate::png_color_reference::png(first.artifact().bytes());
352 assert_eq!(
353 (actual.width() as usize, actual.height() as usize),
354 (row.width, row.height)
355 );
356 assert_eq!(actual.rgba(), row.pixels, "{}", row.id);
357 } else {
358 let mut source = source;
359 source["files"] = serde_json::json!({});
360 let control =
361 crate::SceneDocument::parse(&serde_json::to_vec(&source).unwrap()).unwrap();
362 assert_eq!(
363 first.artifact().bytes(),
364 crate::Renderer::new()
365 .render(&control, &request)
366 .unwrap()
367 .artifact()
368 .bytes()
369 );
370 }
371 }
372 }
373
374 fn png_color_cases_in_wasm(evidence: serde_json::Value) {
375 for row in evidence["cases"].as_array().unwrap() {
376 let scene =
377 crate::SceneDocument::parse(&serde_json::to_vec(&row["scene"]).unwrap()).unwrap();
378 let request = crate::RenderRequest::builder(crate::OutputFormat::Png)
379 .padding(0.0)
380 .include_background(false)
381 .build()
382 .unwrap();
383 let first = crate::Renderer::new().render(&scene, &request).unwrap();
384 let second = crate::Renderer::new().render(&scene, &request).unwrap();
385 assert_eq!(first.artifact().bytes(), second.artifact().bytes());
386 assert_eq!(
387 serde_json::to_vec(first.report()).unwrap(),
388 serde_json::to_vec(second.report()).unwrap()
389 );
390 if row["group"] == "composition" {
391 let late = crate::SceneDocument::parse(
392 &serde_json::to_vec(&crate::png_stage4_review::late_scene(row)).unwrap(),
393 )
394 .unwrap();
395 let late = crate::Renderer::new().render(&late, &request).unwrap();
396 assert_eq!(first.artifact().bytes(), late.artifact().bytes());
397 assert_eq!(first.report(), late.report());
398 }
399 if row["group"] == "composition" {
400 crate::png_stage4_composition::report(
401 row,
402 "png",
403 serde_json::to_value(first.report()).unwrap(),
404 );
405 } else {
406 crate::png_color_reference::report(
407 row,
408 "png",
409 serde_json::to_value(first.report()).unwrap(),
410 );
411 }
412 if let Some(equivalent) = crate::png_color_reference::bt1886_equivalent(&evidence, row)
413 .or_else(|| crate::png_color_reference::ignored_cicp_equivalent(&evidence, row))
414 .or_else(|| crate::png_color_reference::gain_map_equivalent(&evidence, row))
415 {
416 let scene =
417 crate::SceneDocument::parse(&serde_json::to_vec(&equivalent["scene"]).unwrap())
418 .unwrap();
419 let control = crate::Renderer::new().render(&scene, &request).unwrap();
420 assert_eq!(
421 first.artifact().bytes(),
422 control.artifact().bytes(),
423 "{} exact metadata equivalence",
424 row["id"]
425 );
426 assert_eq!(first.report(), control.report());
427 }
428 if crate::png_color_reference::recovery(row, "png").is_some() {
429 let mut control = row["scene"].clone();
430 control["files"] = serde_json::json!({});
431 let control =
432 crate::SceneDocument::parse(&serde_json::to_vec(&control).unwrap()).unwrap();
433 assert_eq!(
434 first.artifact().bytes(),
435 crate::Renderer::new()
436 .render(&control, &request)
437 .unwrap()
438 .artifact()
439 .bytes()
440 );
441 } else {
442 crate::png_stage4_reference::anchor(
443 row,
444 crate::png_color_reference::png(first.artifact().bytes()).rgba(),
445 );
446 crate::png_color_reference::appearance(
447 row,
448 crate::png_color_reference::png(first.artifact().bytes()),
449 false,
450 );
451 }
452 }
453 }
454
455 #[wasm_bindgen_test]
456 fn png_precision_reference_in_wasm() {
457 use base64::Engine as _;
458 use sha2::{Digest, Sha256};
459 let bytes = include_bytes!("../../../tests/oracle/png-precision/reference.json");
460 let evidence: serde_json::Value = serde_json::from_slice(bytes).unwrap();
461 let decode_png = |bytes: &[u8]| {
462 let mut reader = png::Decoder::new(std::io::Cursor::new(bytes))
463 .read_info()
464 .unwrap();
465 let mut rgba = vec![0; reader.output_buffer_size().expect("PNG buffer fits")];
466 let info = reader.next_frame(&mut rgba).unwrap();
467 assert_eq!(info.color_type, png::ColorType::Rgba);
468 assert_eq!(info.bit_depth, png::BitDepth::Eight);
469 rgba.truncate(info.buffer_size());
470 (info.width, info.height, rgba)
471 };
472 for row in evidence["cases"].as_array().unwrap() {
473 let scene =
474 crate::SceneDocument::parse(&serde_json::to_vec(&row["scene"]).unwrap()).unwrap();
475 let request = crate::RenderRequest::builder(crate::OutputFormat::Png)
476 .padding(0.0)
477 .include_background(row["options"]["exportBackground"].as_bool().unwrap())
478 .build()
479 .unwrap();
480 let first = crate::Renderer::new().render(&scene, &request).unwrap();
481 let second = crate::Renderer::new().render(&scene, &request).unwrap();
482 assert_eq!(first.artifact().bytes(), second.artifact().bytes());
483 assert_eq!(first.report(), second.report());
484 assert_eq!(
485 serde_json::to_value(first.report()).unwrap(),
486 serde_json::json!({"schema_version":1,"diagnostics":[]})
487 );
488 let expected = base64::engine::general_purpose::STANDARD
489 .decode(row["png_base64"].as_str().unwrap())
490 .unwrap();
491 assert_eq!(
492 decode_png(first.artifact().bytes()),
493 decode_png(&expected),
494 "{}",
495 row["id"]
496 );
497 }
498 }
499
500 #[wasm_bindgen_test]
501 fn complete_font_subset_qualification_corpus_in_wasm() {
502 crate::render::assert_complete_font_subset_qualification_corpus();
503 }
504
505 #[wasm_bindgen_test]
506 fn font_subset_fuzz_regression_corpus_in_wasm() {
507 crate::render::assert_font_subset_fuzz_regression_corpus();
508 }
509
510 #[wasm_bindgen_test]
511 fn font_subset_fuzz_fixed_seed_smoke_in_wasm() {
512 crate::render::assert_font_subset_fuzz_smoke();
513 }
514
515 #[wasm_bindgen_test]
516 fn font_subset_resource_trace_matrix_in_wasm() {
517 crate::render::assert_font_subset_resource_trace_matrix();
518 }
519
520 #[wasm_bindgen_test]
521 fn scene_fuzz_regression_corpus_in_wasm() {
522 crate::scene_fuzz::assert_scene_fuzz_regression_corpus();
523 }
524
525 #[wasm_bindgen_test]
526 fn scene_fuzz_fixed_seed_smoke_in_wasm() {
527 crate::scene_fuzz::assert_scene_fuzz_smoke();
528 }
529}
530
531pub const fn license_bundle() -> &'static str {
533 include_str!("../LICENSE-BUNDLE.txt")
534}