Search code examples
csscsvgcairolibrsvg

librsvg rsvg_handle_get_dimensions get pixel size is not different with render size in browser


librsvg verions is 2.40.20

I want to convert .svg file to .png file, so I write some codes.

the generated function is very simple.

int svg_file2png_file(char* svg_path, char* png_path) {
    GError* error = NULL;
    RsvgHandle* handle = rsvg_handle_new_from_file(svg_path, &error);
    if (error) {
        printf("error\n");
        return -1;
    }
    if (handle == NULL) {
        printf("rsvg_handle_new_from_data return NULL\n");
        return -1;
    }

    RsvgDimensionData dimension = { 0 };
    rsvg_handle_get_dimensions(handle, &dimension);
    
    cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, dimension.width, dimension.height);
    if (surface == NULL) {
        printf("cairo_image_surface_create Failed\n");
        return -1;
    }

    cairo_t* cr = cairo_create(surface);
    if (cr == NULL) {
        printf("cairo_create Failed\n");
        return -1;
    }

    if (rsvg_handle_render_cairo(handle, cr) == 0) {
        printf("rsvg_handle_render_cairo Failed\n");
        return -1;
    }

    cairo_status_t status = cairo_surface_write_to_png(surface, "./cairo_output.png");
    if (status != CAIRO_STATUS_SUCCESS) {
        printf("cairo_surface_write_to_png Failed\n");
        return -1;
    }
    printf("svg convert to png success\n");

    cairo_destroy(cr);
    cr = NULL;
    cairo_surface_destroy(surface);
    surface = NULL;
    rsvg_handle_free(handle);
    handle = NULL;

    return 0;
}

the svg render size in browser is (width)379px,(height)21px(I ignore the decimal part).

the generated image size is (width)264px,(height)15px.

The generated image is somewhat blurry, but SVG renders very clearly on the browser.

the svg file:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
  xmlns="http://www.w3.org/2000/svg"
  xmlns:xlink="http://www.w3.org/1999/xlink"
  style="vertical-align: -0.566ex;"
  width="43.923ex"
  height="2.452ex"
  viewBox="0 -833.9 19413.9 1083.9"
  role="img"
  focusable="false"
  aria-hidden="true">
  <defs>
    <path id="MJX-22923815-TEX-N-39" d="M352 287Q304 211 232 211Q154 211 104 270T44 396Q42 412 42 436V444Q42 537 111 606Q171 666 243 666Q245 666 249 666T257 665H261Q273 665 286 663T323 651T370 619T413 560Q456 472 456 334Q456 194 396 97Q361 41 312 10T208 -22Q147 -22 108 7T68 93T121 149Q143 149 158 135T173 96Q173 78 164 65T148 49T135 44L131 43Q131 41 138 37T164 27T206 22H212Q272 22 313 86Q352 142 352 280V287ZM244 248Q292 248 321 297T351 430Q351 508 343 542Q341 552 337 562T323 588T293 615T246 625Q208 625 181 598Q160 576 154 546T147 441Q147 358 152 329T172 282Q197 248 244 248Z"></path>
    <path id="MJX-22923815-TEX-I-1D44E" d="M33 157Q33 258 109 349T280 441Q331 441 370 392Q386 422 416 422Q429 422 439 414T449 394Q449 381 412 234T374 68Q374 43 381 35T402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487Q506 153 506 144Q506 138 501 117T481 63T449 13Q436 0 417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157ZM351 328Q351 334 346 350T323 385T277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q217 26 254 59T298 110Q300 114 325 217T351 328Z"></path>
    <path id="MJX-22923815-TEX-N-32" d="M109 429Q82 429 66 447T50 491Q50 562 103 614T235 666Q326 666 387 610T449 465Q449 422 429 383T381 315T301 241Q265 210 201 149L142 93L218 92Q375 92 385 97Q392 99 409 186V189H449V186Q448 183 436 95T421 3V0H50V19V31Q50 38 56 46T86 81Q115 113 136 137Q145 147 170 174T204 211T233 244T261 278T284 308T305 340T320 369T333 401T340 431T343 464Q343 527 309 573T212 619Q179 619 154 602T119 569T109 550Q109 549 114 549Q132 549 151 535T170 489Q170 464 154 447T109 429Z"></path>
    <path id="MJX-22923815-TEX-N-28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path>
    <path id="MJX-22923815-TEX-I-1D465" d="M52 289Q59 331 106 386T222 442Q257 442 286 424T329 379Q371 442 430 442Q467 442 494 420T522 361Q522 332 508 314T481 292T458 288Q439 288 427 299T415 328Q415 374 465 391Q454 404 425 404Q412 404 406 402Q368 386 350 336Q290 115 290 78Q290 50 306 38T341 26Q378 26 414 59T463 140Q466 150 469 151T485 153H489Q504 153 504 145Q504 144 502 134Q486 77 440 33T333 -11Q263 -11 227 52Q186 -10 133 -10H127Q78 -10 57 16T35 71Q35 103 54 123T99 143Q142 143 142 101Q142 81 130 66T107 46T94 41L91 40Q91 39 97 36T113 29T132 26Q168 26 194 71Q203 87 217 139T245 247T261 313Q266 340 266 352Q266 380 251 392T217 404Q177 404 142 372T93 290Q91 281 88 280T72 278H58Q52 284 52 289Z"></path>
    <path id="MJX-22923815-TEX-N-2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"></path>
    <path id="MJX-22923815-TEX-I-1D466" d="M21 287Q21 301 36 335T84 406T158 442Q199 442 224 419T250 355Q248 336 247 334Q247 331 231 288T198 191T182 105Q182 62 196 45T238 27Q261 27 281 38T312 61T339 94Q339 95 344 114T358 173T377 247Q415 397 419 404Q432 431 462 431Q475 431 483 424T494 412T496 403Q496 390 447 193T391 -23Q363 -106 294 -155T156 -205Q111 -205 77 -183T43 -117Q43 -95 50 -80T69 -58T89 -48T106 -45Q150 -45 150 -87Q150 -107 138 -122T115 -142T102 -147L99 -148Q101 -153 118 -160T152 -167H160Q177 -167 186 -165Q219 -156 247 -127T290 -65T313 -9T321 21L315 17Q309 13 296 6T270 -6Q250 -11 231 -11Q185 -11 150 11T104 82Q103 89 103 113Q103 170 138 262T173 379Q173 380 173 381Q173 390 173 393T169 400T158 404H154Q131 404 112 385T82 344T65 302T57 280Q55 278 41 278H27Q21 284 21 287Z"></path>
    <path id="MJX-22923815-TEX-N-29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path>
    <path id="MJX-22923815-TEX-N-33" d="M127 463Q100 463 85 480T69 524Q69 579 117 622T233 665Q268 665 277 664Q351 652 390 611T430 522Q430 470 396 421T302 350L299 348Q299 347 308 345T337 336T375 315Q457 262 457 175Q457 96 395 37T238 -22Q158 -22 100 21T42 130Q42 158 60 175T105 193Q133 193 151 175T169 130Q169 119 166 110T159 94T148 82T136 74T126 70T118 67L114 66Q165 21 238 21Q293 21 321 74Q338 107 338 175V195Q338 290 274 322Q259 328 213 329L171 330L168 332Q166 335 166 348Q166 366 174 366Q202 366 232 371Q266 376 294 413T322 525V533Q322 590 287 612Q265 626 240 626Q208 626 181 615T143 592T132 580H135Q138 579 143 578T153 573T165 566T175 555T183 540T186 520Q186 498 172 481T127 463Z"></path>
    <path id="MJX-22923815-TEX-N-3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path>
    <path id="MJX-22923815-TEX-I-1D440" d="M289 629Q289 635 232 637Q208 637 201 638T194 648Q194 649 196 659Q197 662 198 666T199 671T201 676T203 679T207 681T212 683T220 683T232 684Q238 684 262 684T307 683Q386 683 398 683T414 678Q415 674 451 396L487 117L510 154Q534 190 574 254T662 394Q837 673 839 675Q840 676 842 678T846 681L852 683H948Q965 683 988 683T1017 684Q1051 684 1051 673Q1051 668 1048 656T1045 643Q1041 637 1008 637Q968 636 957 634T939 623Q936 618 867 340T797 59Q797 55 798 54T805 50T822 48T855 46H886Q892 37 892 35Q892 19 885 5Q880 0 869 0Q864 0 828 1T736 2Q675 2 644 2T609 1Q592 1 592 11Q592 13 594 25Q598 41 602 43T625 46Q652 46 685 49Q699 52 704 61Q706 65 742 207T813 490T848 631L654 322Q458 10 453 5Q451 4 449 3Q444 0 433 0Q418 0 415 7Q413 11 374 317L335 624L267 354Q200 88 200 79Q206 46 272 46H282Q288 41 289 37T286 19Q282 3 278 1Q274 0 267 0Q265 0 255 0T221 1T157 2Q127 2 95 1T58 0Q43 0 39 2T35 11Q35 13 38 25T43 40Q45 46 65 46Q135 46 154 86Q158 92 223 354T289 629Z"></path>
    <path id="MJX-22923815-TEX-N-22C5" d="M78 250Q78 274 95 292T138 310Q162 310 180 294T199 251Q199 226 182 208T139 190T96 207T78 250Z"></path>
    <path id="MJX-22923815-TEX-N-2B" d="M56 237T56 250T70 270H369V420L370 570Q380 583 389 583Q402 583 409 568V270H707Q722 262 722 250T707 230H409V-68Q401 -82 391 -82H389H387Q375 -82 369 -68V230H70Q56 237 56 250Z"></path>
    <path id="MJX-22923815-TEX-N-2C" d="M78 35T78 60T94 103T137 121Q165 121 187 96T210 8Q210 -27 201 -60T180 -117T154 -158T130 -185T117 -194Q113 -194 104 -185T95 -172Q95 -168 106 -156T131 -126T157 -76T173 -3V9L172 8Q170 7 167 6T161 3T152 1T140 0Q113 0 96 17Z"></path>
  </defs>
  <g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)">
    <g>
      <g>
        <use  xlink:href="#MJX-22923815-TEX-N-39"></use>
      </g>
      <g transform="translate(500,0)">
        <g>
          <use xlink:href="#MJX-22923815-TEX-I-1D44E"></use>
        </g>
        <g transform="translate(562,363) scale(0.707)" data-mjx-texclass="ORD">
          <g>
            <use xlink:href="#MJX-22923815-TEX-N-32"></use>
          </g>
        </g>
      </g>
      <g transform="translate(1465.6,0)">
        <use xlink:href="#MJX-22923815-TEX-N-28"></use>
      </g>
      <g transform="translate(1854.6,0)">
        <use xlink:href="#MJX-22923815-TEX-I-1D465"></use>
      </g>
      <g transform="translate(2648.8,0)">
        <use xlink:href="#MJX-22923815-TEX-N-2212"></use>
      </g>
      <g transform="translate(3649,0)">
        <use xlink:href="#MJX-22923815-TEX-I-1D466"></use>
      </g>
      <g transform="translate(4139,0)">
        <g>
          <use xlink:href="#MJX-22923815-TEX-N-29"></use>
        </g>
        <g transform="translate(422,363) scale(0.707)" data-mjx-texclass="ORD">
          <g>
            <use xlink:href="#MJX-22923815-TEX-N-32"></use>
          </g>
        </g>
      </g>
      <g transform="translate(5186.8,0)">
        <use xlink:href="#MJX-22923815-TEX-N-2212"></use>
      </g>
      <g transform="translate(6187,0)">
        <use xlink:href="#MJX-22923815-TEX-N-33"></use>
      </g>
      <g transform="translate(6687,0)">
        <use xlink:href="#MJX-22923815-TEX-I-1D44E"></use>
      </g>
      <g transform="translate(7216,0)">
        <use xlink:href="#MJX-22923815-TEX-N-28"></use>
      </g>
      <g transform="translate(7605,0)">
        <use xlink:href="#MJX-22923815-TEX-I-1D466"></use>
      </g>
      <g transform="translate(8317.2,0)">
        <use xlink:href="#MJX-22923815-TEX-N-2212"></use>
      </g>
      <g transform="translate(9317.4,0)">
        <use xlink:href="#MJX-22923815-TEX-I-1D465"></use>
      </g>
      <g transform="translate(9889.4,0)">
        <g>
          <use xlink:href="#MJX-22923815-TEX-N-29"></use>
        </g>
        <g transform="translate(422,363) scale(0.707)" data-mjx-texclass="ORD">
          <g>
            <use xlink:href="#MJX-22923815-TEX-N-33"></use>
          </g>
        </g>
      </g>
      <g transform="translate(10992.8,0)">
        <use xlink:href="#MJX-22923815-TEX-N-3D"></use>
      </g>
      <g transform="translate(12048.5,0)">
        <use xlink:href="#MJX-22923815-TEX-I-1D440"></use>
      </g>
      <g transform="translate(13321.8,0)">
        <use xlink:href="#MJX-22923815-TEX-N-22C5"></use>
      </g>
      <g transform="translate(13822,0)">
        <use xlink:href="#MJX-22923815-TEX-N-28"></use>
      </g>
      <g transform="translate(14211,0)">
        <use xlink:href="#MJX-22923815-TEX-N-33"></use>
      </g>
      <g transform="translate(14711,0)">
        <use xlink:href="#MJX-22923815-TEX-I-1D44E"></use>
      </g>
      <g transform="translate(15462.2,0)">
        <use xlink:href="#MJX-22923815-TEX-N-2B"></use>
      </g>
      <g transform="translate(16462.4,0)">
        <use xlink:href="#MJX-22923815-TEX-I-1D465"></use>
      </g>
      <g transform="translate(17256.7,0)">
        <use xlink:href="#MJX-22923815-TEX-N-2212"></use>
      </g>
      <g transform="translate(18256.9,0)">
        <use xlink:href="#MJX-22923815-TEX-I-1D466"></use>
      </g>
      <g transform="translate(18746.9,0)">
        <use xlink:href="#MJX-22923815-TEX-N-29"></use>
      </g>
      <g transform="translate(19135.9,0)">
        <use xlink:href="#MJX-22923815-TEX-N-2C"></use>
      </g>
    </g>
  </g>
</svg>

The function of rsvg_handle_get_dimensions() return dimension's size is smaller than render size in browser.

I know this issue may be related to the use of relative length(ex) in the SVG file, but I dot't know how to solve it.


Solution

  • I rewrite my comment here:

    According to the W3C SVG 1.1 The em and ex unit identifiers are relative to the current font's font-size and x-height, respectively.

    And according to MDN, x-height is no longer recommended. Though some browsers might still support it, it may have already been removed from the relevant web standards, may be in the process of being dropped, or may only be kept for compatibility purposes. So maybe this happens to your case.

    I don't know, this will help you or not, but it relates to your problem:

    From Rsvg documentation:

    <svg xmlns="http://www.w3.org/2000/svg" width="10em" height="5em">
      <style>
        * { font-size: 2cm; }
      </style>
    

    This means that the width is 10 times the font size of 2cm, and the height is 5 times the font size.

    In general an application cannot figure this out easily, since it would need a CSS parser and cascading engine to even be able to know what the font size is for the toplevel <svg>. Fortunately, librsvg already does that!

    In all those cases, the width and height are in physical units (px, cm, mm, etc.), or font-based units (em, ex) that can be resolved to pixels. You can use rsvg_handle_get_intrinsic_size_in_pixels() to do the conversion easily if all you want to do is to create a surface with the “natural” number of pixels:

    gboolean rsvg_handle_get_intrinsic_size_in_pixels (RsvgHandle *handle,
                                                       gdouble    *out_width,
                                                       gdouble    *out_height);
    

    However, the documentation for that function indicates that it may return FALSE in some cases. Let’s see what those ugly cases are.

    This function is able to extract the size in pixels from an SVG document if the document has both width and height attributes with physical units (px, in, cm, mm, pt, pc) or font-based units (em, ex). For physical units, the dimensions are normalized to pixels using the dots-per-inch (DPI) value set previously with rsvg_handle_set_dpi(). For font-based units, this function uses the computed value of the font-size property for the toplevel <svg> element. In those cases, this function returns TRUE.

    This function is not able to extract the size in pixels directly from the intrinsic dimensions of the SVG document if the width or height are in percentage units (or if they do not exist, in which case the SVG spec mandates that they default to 100%), as these require a viewport to be resolved to a final size. In this case, the function returns FALSE.

    Instead of querying an SVG document’s size, applications are encouraged to render SVG documents to a size chosen by the application, by passing a suitably-sized viewport to rsvg_handle_render_document().

    EDIT1:

    After the answer is accepted, I find the solution:

    1. OP used old version of librsvg, I'm on version 2.58.0
    2. As said above, the SVG file used ex unit for its width and height. I checked my Google Chrome and it renders the 351.375 x 19.609 pixels. Your browser may differ. I checked my Google Chrome's default font size is 16. So I added * { font-size: 16px } to my rsvg handler and generated png file has same dimension as my google chrome.

    Here is the code:

    #include <stdlib.h>
    #include <librsvg/rsvg.h>
    
    int main (int argc, char **argv) {
      if (argc != 3) {
        g_printerr ("Usage error! %s infile.svg outfile.png\n", argv[0]);
        return EXIT_FAILURE;
      }
    
      const gchar *svg_path = argv[1];
      const gchar *png_path = argv[2];
    
      GError *error = NULL;
      RsvgHandle *handle = rsvg_handle_new_from_file (svg_path, &error);
      if (!handle) {
        g_printerr ("Error reading SVG file: %s\n", error->message);
        g_error_free (error);
        return EXIT_FAILURE;
      }
    
      const gchar *css = "* { font-size: 16px; }";
      error = NULL;
      if (!rsvg_handle_set_stylesheet (handle, (guint8 *) css, strlen (css), &error)) {
        g_printerr ("Error setting SVG stylesheet: %s\n", error->message);
        g_error_free (error);
        g_object_unref (handle);
        return EXIT_FAILURE;
      }
    
      RsvgRectangle viewport = { 0.0, 0.0, 0.0, 0.0 };
      if (!rsvg_handle_get_intrinsic_size_in_pixels (handle, &viewport.width, &viewport.height)) {
        g_printerr ("Error getting SVG size in pixels!\n");
        g_object_unref (handle);
        return EXIT_FAILURE;
      }
      g_print ("%f x %f\n", viewport.width, viewport.height);
    
      cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, viewport.width, viewport.height);
      cairo_t *cr = cairo_create (surface);
    
      if (!rsvg_handle_render_document (handle, cr, &viewport, &error)) {
        g_printerr ("Could not render: %s\n", error->message);
        g_error_free (error);
        cairo_destroy (cr);
        cairo_surface_destroy (surface);
        g_object_unref (handle);
        return EXIT_FAILURE;
      }
    
      if (cairo_surface_write_to_png (surface, png_path) != CAIRO_STATUS_SUCCESS) {
        g_printerr ("Could not write output file: %s\n", png_path);
        cairo_destroy (cr);
        cairo_surface_destroy (surface);
        g_object_unref (handle);
        return EXIT_FAILURE;
      }
    
      cairo_destroy (cr);
      cairo_surface_destroy (surface);
      g_object_unref (handle);
    
      return EXIT_SUCCESS;
    }
    

    EDIT2

    libsvg has a util named librsvg-convert. So you don't need to write custom code to convert SVG to PNG. Create style.css:

    * {
      font-size: 16px;
    }
    

    And then run

    rsvg-convert --stylesheet style.css infile.svg > outfile.png