Repository: typst/typst
File: crates/typst/src/visualize/image/mod.rs (Rust)

Code before:
#[typst_macros::time(span = elem.span())]
fn layout_image(
    elem: &Packed<ImageElem>,
    engine: &mut Engine,
    _: Locator,
    styles: StyleChain,
    region: Region,
) -> SourceResult<Frame> {
    let span = elem.span();

    // Take the format that was explicitly defined, or parse the extension,
    // or try to detect the format.
    let data = elem.data();
    let format = match elem.format(styles) {
        Smart::Custom(v) => v,
        Smart::Auto => determine_format(elem.path().as_str(), data).at(span)?,
    };

    // Warn the user if the image contains a foreign object. Not perfect
    // because the svg could also be encoded, but that's an edge case.
    if format == ImageFormat::Vector(VectorFormat::Svg) {
        let needle = b"<foreignObject";
        if data
            .as_slice()
            .windows(needle.len())
            .any(|s| s == needle)
        {
            engine.sink.warn(warning!(span, "image contains foreign object";
            hint: "svg images with foreign objects might render incorrectly in typst";
            hint: "see https://github.com/typst/typst/issues/1421 for more information"));
        }
    }

    // Construct the image itself.
    let image = Image::with_fonts(
        data.clone().into(),
        format,
        elem.alt(styles),
        engine.world,
        &families(styles).collect::<Vec<_>>(),
    )
    .at(span)?;

    // Determine the image's pixel aspect ratio.
    let pxw = image.width();
    let pxh = image.height();
    let px_ratio = pxw / pxh;

    // Determine the region's aspect ratio.
    let region_ratio = region.size.x / region.size.y;


Review comment (performance):
This seems potentially rather slow. I think `std::str::from_utf8` + `contains` might actually be faster because `str::contains` uses an optimized string search algorithm. We could also use https://docs.rs/memmem but it feels like overkill to pull a crate.

Code after:
fn layout_image(
    elem: &Packed<ImageElem>,
    engine: &mut Engine,
    _: Locator,
    styles: StyleChain,
    region: Region,
) -> SourceResult<Frame> {
    let span = elem.span();

    // Take the format that was explicitly defined, or parse the extension,
    // or try to detect the format.
    let data = elem.data();
    let format = match elem.format(styles) {
        Smart::Custom(v) => v,
        Smart::Auto => determine_format(elem.path().as_str(), data).at(span)?,
    };

    // Warn the user if the image contains a foreign object. Not perfect
    // because the svg could also be encoded, but that's an edge case.
    if format == ImageFormat::Vector(VectorFormat::Svg) {
        let has_foreign_object =
            data.as_str().is_some_and(|s| s.contains("<foreignObject"));

        if has_foreign_object {
            engine.sink.warn(warning!(
                span,
                "image contains foreign object";
                hint: "SVG images with foreign objects might render incorrectly in typst";
                hint: "see https://github.com/typst/typst/issues/1421 for more information"
            ));
        }
    }

    // Construct the image itself.
    let image = Image::with_fonts(
        data.clone().into(),
        format,
        elem.alt(styles),
        engine.world,
        &families(styles).collect::<Vec<_>>(),
    )
    .at(span)?;

    // Determine the image's pixel aspect ratio.
    let pxw = image.width();
    let pxh = image.height();
    let px_ratio = pxw / pxh;

    // Determine the region's aspect ratio.
    let region_ratio = region.size.x / region.size.y;