Skip to main content

Command Palette

Search for a command to run...

Try to contribute to opensource #1 librsvg

Published
7 min read
N

A hobby developer, he love explore new things and sharing his note with others.

I have noticed that someone wants newbie help for some open source from a local community member.

I follow the link to Mustodon's post.

found out that it was a librsvg issue on the gnome GitLab repository that has hashtag for rust newbies. It will be good for me since I was new to the project.

Read the docs

I was surprised by the document pages that were so informative.

Use on mac

Local machine

I start out by using git clone to a Mac m1 since the architecture was different from the GETTING STARTED AS A CONTRIBUTOR - Setting up your development environment that using x86_64 and openSUSE Tumbleweed, I was curious that could it be buildable on the local machine.

I couldn't build on the local machine due to the version of dependency for the setup development environment. As I'm writing this the development environment requires cairo as a system library with version 1.17 or above but the machine has cairo with version 1.16.0 via brew installed via brew.

$ cargo test
  --- stderr
  `PKG_CONFIG_ALLOW_SYSTEM_CFLAGS="1" "pkg-config" "--libs" "--cflags" "cairo" "cairo >= 1.17"` did not exit successfully: exit status: 1
  error: could not find system library 'cairo' required by the 'librsvg' crate

  --- stderr
  Requested 'cairo >= 1.17' but version of cairo is 1.16.0

warning: build failed, waiting for other jobs to finish...

librsvg > brew install cairo@1.17.2
Warning: No available formula with the name "cairo@1.17.2". Did you mean cairomm@1.14?
==> Searching for similarly named formulae and casks...

I don't want to mess with the local machine packages so I try to use podman as development.

Podman

Start with installing podman with brew.

$ brew install podman
==> Fetching podman
...
==> Pouring podman--4.5.1.arm64_monterey.bottle.tar.gz

Setup initial machine on podman.

$ podman machine init
Downloading VM image: fedora-coreos-38.20230609.2.1-qemu.aarch64.qcow2.xz: done
Extracting compressed file
Image resized.
Machine init complete
$ podman machine start

Pull machine for librsvg development.

$ sh ci/pull-container-image.sh
full_tag="x86_64-1.68.2-2023-05-03.0-main"
pulling image registry.gitlab.gnome.org/gnome/librsvg/opensuse/tumbleweed:x86_64-1.68.2-2023-05-03.0-main
...
WARNING: image platform (linux/amd64) does not match the expected platform (linux/arm64)
...
You can now run this:
  podman run --rm -ti --cap-add=SYS_PTRACE -v $(pwd):/srv/project -w /srv/project registry.gitlab.gnome.org/gnome/librsvg/opensuse/tumbleweed:x86_64-1.68.2-2023-05-03.0-main

Don't forget to run this once inside the container:
  source ci/env.sh

After I run as the command output, I got an error when using the test. It seems to be a container failed to connect a network error message.

$ podman run --rm -ti --cap-add=SYS_PTRACE -v $(pwd):/srv/project -w /srv/project registry.gitlab.gnome.org/gnome/librsvg/opensuse/tumbleweed:x86_64-1.68.2-2023-05-03.0-main
:/srv/project # source ci/env.sh
:/srv/project # cargo test
    Updating crates.io index
warning: spurious network error (2 tries remaining): failed to mmap. Could not write data: Invalid argument; class=Os (2)
warning: spurious network error (1 tries remaining): failed to mmap. Could not write data: Invalid argument; class=Os (2)
error: Unable to update registry `crates-io`

I find this issue that could fix it with specifying slirp4netns:port_handler=slirp4netns as the network option value.

$ podman run --rm -ti --network=slirp4netns:port_handler=slirp4netns --cap-add=SYS_PTRACE -v $(pwd):/srv/project -w /srv/project registry.gitlab.gnome.org/gnome/librsvg/opensuse/tumbleweed:x86_64-1.68.2-2023-05-03.0-main

A build-in git seems a bit off so I use executable instead.

:/srv/project # source ci/env.sh
:/srv/project # cargo test
    Updating crates.io index
warning: spurious network error (1 tries remaining): failed to resolve address for github.com: Name or service not known; class=Net (12)

After setup for a while, I could run a test but I got the error message after that.

:/srv/project # source ci/env.sh
:/srv/project # CARGO_NET_GIT_FETCH_WITH_CLI=true cargo test
error[E0786]: found invalid metadata files for crate `itertools`
 --> rsvg/src/space.rs:3:5
  |
3 | use itertools::Itertools;
  |     ^^^^^^^^^
  |
  = note: failed to mmap rmeta metadata: '/srv/project/target/debug/deps/libitertools-72978d5f52e1ee56.rmeta'

error: could not compile `librsvg` due to previous error

Setup VM

I was concerned that it might be an architecture or package on architecture problem so I switched to using VMware on the x86_64 machine.

  • Download the latest Opensesu Tumbleweed with x86_64 architecture.

  • Open a new machine with ram 8 gb as recommended.

  • I used 50 GB disk space since it will expand to this cap when I use up more space.

  • I use Gnome as a display manager.

After the machine was up I install dependency as a guide but I want to use rustup to install formatter so I install rustup instead of rust rust-std cargo. I couldn't install python38-docutils so I removed it.

sudo zypper install -y gcc rustup make \
automake autoconf libtool python3-gi-docgen git \
gdk-pixbuf-devel gobject-introspection-devel \
libxml2-devel cairo-devel pango-devel

Use rustup to set the default toolchain.

rustup default stable

Clone to VMware

After installing packages, I clone the repository and then test. It worked!

git clone https://gitlab.gnome.org/GNOME/librsvg
cargo test

Read the issue

The issue seems to involve rendering new property for SVG image tag with cairo's library.

Follow contribute guide

I set up neovim and tmux a bit in the machine and make changes following the contribution guide

Follow contribute guide

There was comment line for a new property.

// "image-rendering" => (PresentationAttr::Yes, unimplemented),

After reading CSS imgage-rendering property.

I used the macro to create a following with an effort to follow with other property patterns.

rsvg/src/property_defs.rs

make_property!(
    /// `image-rendering` property.
    ///
    /// CSS Images Module Level 3: <https://www.w3.org/TR/css-images-3/#the-image-rendering>
    ///
    /// Note that this property previously accepted the values optimizeSpeed and optimizeQuality.
    /// These are now deprecated; a user agent must accept them as valid values but must treat
    /// them as having the same behavior as crisp-edges and smooth respectively.
    ImageRendering,
    default: Auto,
    inherits_automatically: true,

    identifiers:
    "auto" => Auto,
    "smooth" => Smooth,
    "optimizeQuality" => OptimizeQuality,
    "high-quality" => HighQuality,
    "crisp-edges" => CrispEdges,
    "optimizeSpeed" => OptimizeSpeed,
    "pixelated" => Pixelated,
);

Uncomment line and use compute following the repository architecture.

rsvg/src/properties.rs

- // "image-rendering"          => (PresentationAttr::Yes, unimplemented),
+ "image-rendering"             => (PresentationAttr::Yes, image_rendering             : ImageRendering),
...
+ compute!(ImageRendering, image_rendering);

Tackling issue

Up to this point was straightforward. Well, After reading I was imagining that an image will be converted as follows.

Tackling issue logic with cairo as suggested

I never use cairo library or librsvg before. After I spent time reading the codebase, I noticed that in drawing function have not passed the values that were computed.

I try using Context.set_antialias() in draw function as mentioned in the issue discussed. I have to make the draw function notice the image-rendering value so I modify the image layout and pass it to the image in draw when Implement ElementTrait for Image since the draw_ctx.draw_layer didn't get a computed value.

Now I got image-rendering add the following code before the actual draw to set_antialias.

match image.image_rendering {
    ImageRendering::Pixelated
    | ImageRendering::CrispEdges
    | ImageRendering::OptimizeSpeed => {
        dc.cr.set_antialias(cairo::Antialias::None)
    }
    ImageRendering::Smooth
    | ImageRendering::OptimizeQuality
    | ImageRendering::HighQuality => {
        dc.cr.set_antialias(cairo::Antialias::Best)
    }
    ImageRendering::Auto => {
        dc.cr.set_antialias(cairo::Antialias::Default)
    }
}

I compile and test converting an image then the image was as same as before. After I searched I find that there are issues on Git Hub using a cairo filter. I use it and it worked. I still set antialias as a way to pass ImageRendering to paint_surface.

if cr.antialias() == cairo::Antialias::None {
    ptn.set_filter(cairo::Filter::Nearest);
}

After that, I also add support to feImage filter primitive, I use the same approach by trying to pass value and set_filter then manually this with a file that has filter property.

After some try and error, I stage and make a commit.

Testing

I could manually test, but I couldn't figure how to compare the exact png image format. I use png in svg to test. After I render image the image, I try to use the output image in svg input test file and then render it again but the result was not the same.

Feature.md

A feature.md file was mentioned but I didn't find the file.

Push

After I register to Gitlab gnome and set up my ssh key and wait some time, I could fork and change the remote origin then push and open draft PR.

As I push, I could see that I mentioned issue in my commit was noticed in the main repository issue.

I wait for my fork to finish CI before I change my draft PR to actual PR.

I make some mistakes in my commit. I forgot to test after pasting cairo filter from image-rendering to feImage. I forgot to remove my test line. The CI was not passed.

I got back to the editor and make changes then test in my local machine with cargo test then using git commit --amend to change the commit code while I leave the message as same as the previous one with the option --no-edit.

After the CI passed I change PR draft state to ready.

Well, This was my contribution journey so far.