Gregorio Litenstein
Gregorio Litenstein

Reputation: 629

librsvg and cairo; rsvg_handle_render_cairo() fails; what am I doing wrong?

I'm playing around with some code in a project; specifically a function that takes SVG images and makes pngs out of them.

I have this:

    typedef std::vector<uint8_t> BinaryBuffer;

    BinaryBuffer readFile(fs::path const& path) {
        BinaryBuffer ret;
        fs::ifstream f(path, std::ios::binary);
        f.seekg(0, std::ios::end);
        ret.resize(f.tellg());
        f.seekg(0);
        f.read(reinterpret_cast<char*>(ret.data()), ret.size());
        if (!f) throw std::runtime_error("File cannot be read: " + path.string());
        return ret;
    }

void loadSVG(Bitmap& bitmap, fs::path const& filename) {
    double factor = config["graphic/svg_lod"].f();
    // Try to load a cached PNG instead
    if (cache::loadSVG(bitmap, filename, factor)) return;
    std::clog << "image/debug: Loading SVG: " + filename.string() << std::endl;
    // Open the SVG file in librsvg
#if !GLIB_CHECK_VERSION(2, 36, 0)   // Avoid deprecation warnings
    g_type_init();
#endif
    GError* pError = nullptr;
    std::shared_ptr<RsvgHandle> svgHandle(rsvg_handle_new_with_flags(RSVG_HANDLE_FLAG_KEEP_IMAGE_DATA), g_object_unref);
    rsvg_handle_set_base_uri(svgHandle.get(),filename.string().c_str());
    BinaryBuffer data = readFile(filename);
    std::clog << "svg/debug: svg data size is: " << data.size() << std::endl;
    gboolean result = rsvg_handle_write(svgHandle.get(), data.data(), data.size(), &pError);
//  rsvg_handle_new_from_file(filename.string().c_str(), &pError)

    if (result != TRUE) {
        g_error_free(pError);
        throw std::runtime_error("Unable to load " + filename.string());
    }
    else {
        std::clog << "svg/debug: SVG loaded succesfully." << std::endl;
    }
    // Get SVG dimensions
    RsvgDimensionData svgDimension;
    rsvg_handle_get_dimensions(svgHandle.get(), &svgDimension);
    // Prepare the pixel buffer
    std::clog << "svg/debug: svg width is: " << svgDimension.width << ", and height: " << svgDimension.height << std::endl;
    bitmap.resize(svgDimension.width*factor, svgDimension.height*factor);
    bitmap.fmt = pix::INT_ARGB;
    bitmap.linearPremul = true;
    // Raster with Cairo
    std::shared_ptr<cairo_surface_t> surface(
      cairo_image_surface_create_for_data(&bitmap.buf[0], CAIRO_FORMAT_ARGB32, bitmap.width, bitmap.height, bitmap.width * 4),
      cairo_surface_destroy);
    std::shared_ptr<cairo_t> dc(cairo_create(surface.get()), cairo_destroy);
    cairo_scale(dc.get(), factor, factor);
    gboolean renderRes = TRUE;
    renderRes = rsvg_handle_render_cairo(svgHandle.get(), dc.get());
    if (renderRes != TRUE) {
        throw std::runtime_error("Unable to render " + filename.string());
        }
    // Change byte order from BGRA to RGBA
    for (uint32_t *ptr = reinterpret_cast<uint32_t*>(&*bitmap.buf.begin()), *end = ptr + bitmap.buf.size() / 4; ptr < end; ++ptr) {
        uint8_t* pixel = reinterpret_cast<uint8_t*>(ptr);
        uint8_t r = pixel[2], g = pixel[1], b = pixel[0], a = pixel[3];
        pixel[0] = r; pixel[1] = g; pixel[2] = b; pixel[3] = a;
    }
    bitmap.fmt = pix::CHAR_RGBA;
    // Write to cache so that it can be loaded faster the next time
    fs::path cache_filename = cache::constructSVGCacheFileName(filename, factor);
    fs::create_directories(cache_filename.parent_path());
    writePNG(cache_filename, bitmap);
}

But it fails in rsvg_handle_render_cairo... I don't know why. A previous version of the function using rsvg_handle_new_from_file (which doesn't use the BinaryBuffer struct) worked fine. Note, however that essentially the same struct and readFile function is used elsewhere without any issues. And from the debug messages I've dropped in there I can see that the file is indeed getting read. I am also getting correct dimensions from my svg file and there don't seem to be any errors prior to the call to the render function (so I'd assume it's parsed ok) but maybe not?

Upvotes: 2

Views: 691

Answers (1)

Gregorio Litenstein
Gregorio Litenstein

Reputation: 629

The answer was ridiculously simple. I was missing rsvg_handle_close(svgHandle.get(), &pError);

Upvotes: 2

Related Questions