exceljs / exceljs

Excel Workbook Manager

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Image position - stretching image

Trynex opened this issue · comments

Hello everyone. I have a problem with images using exceljs. If I just run excel file through and save it, images are immediately deformed (stretched). I tried workaround and so I just removed images from original excel file and inserted them with add image. Again stretched. So I tried not to specify cells for image to include, but also other(more specific option):

{tl: { col: 1.2, row: 0.4 }, br: { col: 3.2, row: 0.9 }}

This worked when row number was not decimal. Because the example above didn't scale the image
from 0.4 to 0.9 of the cell, but rather deformed it .. Which is weird as the image started the position on 0.4. This for example worked fine:

{tl: { col: 1.2, row: 0.4 }, br: { col: 3.2, row: 1 }}

but any decimal number in br->row does weird things.

I also tried to specify editAs attribute and all its options, but no luck there.

Am I doing something wrong or is it a bug? Is there any workaround where I can specify the place where I want to include image without stretching image?

Thank you for your help!

I'm having a similar issue in where the row property isn't being respected.
In my case though, I want to resize the image.

  // Add the Header Image
  sheet.mergeCells('A1:E1');
  const imageRow = sheet.getRow(1);
  imageRow.height = 60;
  const headerImage = workbook.addImage({
    base64: headerImg,
    extension: 'png',
  });
  sheet.addImage( headerImage, {
    tl: { col: 0.1, row: 0.3},
    br: { col: 2.0, row: 0.99999999999}
  });

screen shot 2018-01-17 at 10 22 59

If I make br: { col: 2.0, row: 1} That pixelated image stretches ALL the way to the full height of Row 1.

Do you see the discrepancy here?
It's as if the height change is being ignored and row doesn't care. The col prop seems to work fine though.

I decided that maybe I could avoid this and pre-size my actual image first and use editAs: 'absolute' but that makes it stretch the image anyway.

There seems to be something fundamentally broken in this function.

Hi man. I know what you mean, I tried everything with this plugin but it didnt work. So I had to fork this package and fix it. Currently I'm not near PC but tomorrow I will write you how to use my modified version to make pictures scalable any way you want.

Sounds good, perhaps we could make a PR with these fixes too? :)

So as promised this is my fork of ExcelJS: https://github.com/Trynex/exceljs
The thing is that (after few hours debugging their code) I found they use fixed height and width of cells to calculate the position of your image. I had to break down excel (unzip it and look into the standard what is excel's structure) and I found out that first you have to specify where your image starts and ends (cell-wise: row and col property of model) and than colOff and rowOff is actuall stretching/position inside the cell of the image. This is what was calculated wrong and I didn't find any possibility to calculate colOff and rowOff easily and dynamically(since you would need to know the width and height of cells you insert the image into). So what I did is I added two new properties colOff and rowOff (obviously) for each of two existing tl and br properties. As you know from documentation tl is top left corner (cell wise) and br is bottom right. But now you need stretching right to specify like part of the cell where you want it.

(TLDR starts here:)

So basic example in original:
{
"tl": { "col": 1, "row": 0},
"br": { "col": 3, "row": 0},
"editAs" : "absolute"
}

But that stretches the wrong way as your cell width and height is not accounted for in calculation. So what you want to do here is to add specific position in the cell where these top left and bottom right corners will start in pixels.

Example with my fix:
{
"tl": { "col": 1, "row": 0, "colOff": 6, "rowOff": 14},
"br": { "col": 3, "row": 0, "colOff": 76, "rowOff": 55},
"editAs" : "absolute"
}

Try to play with it little and ask if you need any help.

@Trynex interesting way of going around this issue.
Would it not be more practical to account the cell height and weight in the calculation or is this something that is not possible?

One can also use the sharp image processing library to add a transparent (or white) background to an image and scale the image whilst retaining it's aspect ratio. Then, placing this square image into a square cell will display it with the correct aspect ratio. Maybe an image is a simpler way of explaining:

screenshot from 2018-03-18 00-11-59

The ratio between width an height in this library seems to be 0.14291498 (IDK why?). In other words, one can set col.width = 10 * 1.429149798 and row.height = 100 to get a square cell. Then, you can use the following sharp snippet to scale the image, preserving the aspect ratio and adding a white background to the image in the places where extra space is needed to keep the image square.

sharp(image)
      .resize(scale, scale)
      .background({r: 255, g: 255, b: 255})
      .embed()
      .toBuffer()
      .then(data => {
        // do something
      })

One could use this snippet to use a transparent background

sharp(image)
      .resize(scale, scale)
      .background({r: 0, g: 0, b: 0, alpha: 0})
      .embed()
      .toFormat(sharp.format.png)
      .toBuffer()
      .then(data => {
        // do something
      })

Super duper hacky but I got a working solution for my use case. Whether or not this will help you depends on your use case.

@Trynex: thanks for your fork. Now I can actually place images without or with only invisible stretch.

Just a quick info for everyone: rowOff and colOff in the open xml specification is not in pixel but actually in EMUs which supposedly stands for English Metric Unit. Here's an article on the topic. @Trynex implemented it so that when you use his code 1 unit in rowOff and colOff is actually 10000 EMU

The row height (as defined with row.height = 10;) is defined in pts. So in my case, where I knew row heights (since I defined it), I could calculate rowoff in pts and then had to multiply it with 1.27 to get 12700 EMUs per pt.

For colOff the thing is even more complex since column with is in some fucked-up font dependend unit. In my case, I got good results with multiplying the cell width (as defined with col.width = 15;) with 7.025 (when using arial).

So, Thanks Microsoft for making a "standard" thats so fucked up that you need to spend hours on just placing images. You could have at least used the same unit for x and y.

I'm just wondering why @Trynex's fix isn't pulled into master. It's an actual bug that editAs: 'absolute' doesn't actually do what it says.

How to export excel ?
i ussed this code

workbook.xlsx.writeBuffer({
base64: true
}).then(buffer => FileSaver.saveAs(new Blob([buffer], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}), ${Date.now()}_feedback.xlsx)).catch(err => console.log('Error writing excel export', err))

but i open excel file and crash, help pls

@FelipeRobles92 It might be because you forgot to quote your file name

 workbook.xlsx.writeBuffer({
            base64: true
        }).then(buffer => FileSaver.saveAs(new Blob([buffer], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}), `${Date.now()}_feedback.xlsx`)).catch(err => console.log('Error writing excel export', err))
    });

it just happens to me when export excel with image :(

I can confirm. This issue is still there. Same problem as OP posted.
//Edited
I added PR for this. @Trynex fix worked for me.

@Trynex @jwmann @dmastag @kaidoj
Hello guys, i fix it in pull request #702,
comparing it to #691, i care about backward compatibility and write some test to ensure it works well.

if You need this changes quicly, i have build branch in my repo:
https://github.com/p3rio/exceljs/tree/build

npm:

    "dependencies": {
        "exceljs": "github:p3rio/exceljs#build"
    }

@Siemienik as soon as i get back to that project where i used exceljs, i will try out your fix and see if that fixes my problem. Ofc its better to have tests etc. I just did it to fix my issue fast without going too deep into execeljs code.

Edit: I can now confirm that @Siemienik fix works for me too.
Edit2: This #702 fix did not actually work for me. Went back to my fork and that works in all excel reading applications.

@alubbe @guyonroche
Hi!
An issue, which was resolved after #702 merged - came back again. Something happened, and images again stretched

An issue, which was resolved after #702 merged - came back again. Something happened, and images again stretched

any one found reason of that?

probably mess in installed dependencies. Can not reproduce it again

So .. does the bug still exists or was it fixed?

as for me, i cannot reproduce it again. So i'd say it's fixed

"editAs" : "absolute"

So as promised this is my fork of ExcelJS: https://github.com/Trynex/exceljs
The thing is that (after few hours debugging their code) I found they use fixed height and width of cells to calculate the position of your image. I had to break down excel (unzip it and look into the standard what is excel's structure) and I found out that first you have to specify where your image starts and ends (cell-wise: row and col property of model) and than colOff and rowOff is actuall stretching/position inside the cell of the image. This is what was calculated wrong and I didn't find any possibility to calculate colOff and rowOff easily and dynamically(since you would need to know the width and height of cells you insert the image into). So what I did is I added two new properties colOff and rowOff (obviously) for each of two existing tl and br properties. As you know from documentation tl is top left corner (cell wise) and br is bottom right. But now you need stretching right to specify like part of the cell where you want it.

(TLDR starts here:)

So basic example in original:
{
"tl": { "col": 1, "row": 0},
"br": { "col": 3, "row": 0},
"editAs" : "absolute"
}

But that stretches the wrong way as your cell width and height is not accounted for in calculation. So what you want to do here is to add specific position in the cell where these top left and bottom right corners will start in pixels.

Example with my fix:
{
"tl": { "col": 1, "row": 0, "colOff": 6, "rowOff": 14},
"br": { "col": 3, "row": 0, "colOff": 76, "rowOff": 55},
"editAs" : "absolute"
}

Try to play with it little and ask if you need any help.

@Trynex How to calculate coloff and rowoff and what is the unit of coloff and rowoff ?

still not working and calculating rowOff & colOff is not clear