Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add \n support for image.print #865

Closed
GDColon opened this issue Mar 27, 2020 · 11 comments
Closed

Add \n support for image.print #865

GDColon opened this issue Mar 27, 2020 · 11 comments
Labels
enhancement a request for a new feature or change in behavior help wanted released This issue/pull request has been released. solution in issue

Comments

@GDColon
Copy link

GDColon commented Mar 27, 2020

Is your feature request related to a problem? Please describe.
I've been using Jimp to render text since I first started JS. It works extremely well most of the time, but I've always been quite bothered by the fact that the \n character simply leaves a small space instead of starting a new line. Considering the maxWidth parameter splits text across multiple lines, I'm not sure why it would be so difficult to implement.

Describe the solution you'd like
Simply allowing \n to be used to start new lines. It can be bypassed by simply replacing all newlines in the string with spaces. Also, perhaps a lineHeight parameter can be passed into the settings to customize how big of a gap to leave between lines.

Describe alternatives you've considered
For now I've been using a simple function I wrote to draw multiline text, in place of image.print. However, I really don't believe this should be necessary.

@Steve-Mcl
Copy link

+1 from me.

I added jimp to node-red via a contrib node node-red-contrib-image-tools
Myself and other users have would love to have support for newline.

@ya5huk
Copy link

ya5huk commented Aug 11, 2021

Writing multiple line text

Though I am late, I wrote a solution for this which can be used in your code.
It is not an implementation of using \n but I believe a lot can use it, because it allows you to write multiline text on an image:
First let's add a function that will break your text into lines in a smart way (without cutting words)

const breakTextToLines = (
  text: string,
  maxCharsPerLine: number,
  minWordsPerLine: number
) => {
  // Function takes text and breaks it to lines where every line is the closest to maxCharsPerLine as possible
  // Line breaks only at ' ' so no words are cut
  const words = text.split(" ");
  let lines: string[] = [];
  let charCount = 0;
  let currLine = "";

  for (const word of words) {
    // every max amount of chars make a new line
    if (charCount + word.length > maxCharsPerLine) {
      lines.push(currLine);
      currLine = "";
      charCount = 0;
    } else if (currLine !== "") {
      // Don't add space at first
      currLine += " ";
    }
    charCount += word.length;
    currLine += word;
  }
  // Add last line that we didn't get to the end of it
  // Optional, add to last line only if it has more than 2 words
  if (currLine.split(" ").length < minWordsPerLine) {
    lines.push(lines.pop() + " " + currLine);
  } else {
    lines.push(currLine);
  }

  if (lines.length === 0) {
    // Which means text is shorter than MAX_CHARS_PER_LINE
    lines.push(currLine);
  }
  return lines;
};

Add text-adding function:

const addTextToImage = async (
  image: Jimp,
  text: string,
  spaceBetweenLines: number,
  font: any
) => {
  // font isn't any but because this is typescript I got no option to get 'Font' type
  const lines = breakTextToLines(text, Constants.MAX_CHARS_PER_LINE, 2);
  let textWidth = Jimp.measureText(font, lines[0]);
  const singleLineHeight = Jimp.measureTextHeight(font, 'a', textWidth); // Just to have the height of a line, could be any char
  const allTextHeight = singleLineHeight * lines.length; // Used to center the whole text
  image.print(
    font,
    // Centering:
    Constants.IMAGE_SIZE / 2 - textWidth / 2,
    Constants.IMAGE_SIZE / 2 - allTextHeight / 2,
    lines[0],
    textWidth,
    singleLineHeight,
    (err, image, { y }) => {
      if (err) {
        throw err;
      }
      for (let i = 1; i < lines.length; i++) {
        // Add all lines, everytime drop lineHeight + spaceBetweenLines
        textWidth = Jimp.measureText(font, lines[i]);
        image.print(
          font,
          Constants.IMAGE_SIZE / 2 - textWidth / 2,
          y + singleLineHeight * (i - 1) + spaceBetweenLines * i, // Formula explanation: i - 1 because at first we get the bottom of the line and then we just calculate the others (from the top so we need to add the whole line value)
          lines[i],
          textWidth,
          singleLineHeight
        );
      }
    }
  );
};

Notice I am centering the text here but you can use it for other cases, just take { x, y } instead of { y } when entering the loop and change the start position when printing the first line.

@prioarief
Copy link

Is your feature request related to a problem? Please describe.
I've been using Jimp to render text since I first started JS. It works extremely well most of the time, but I've always been quite bothered by the fact that the \n character simply leaves a small space instead of starting a new line. Considering the maxWidth parameter splits text across multiple lines, I'm not sure why it would be so difficult to implement.

Describe the solution you'd like
Simply allowing \n to be used to start new lines. It can be bypassed by simply replacing all newlines in the string with spaces. Also, perhaps a lineHeight parameter can be passed into the settings to customize how big of a gap to leave between lines.

Describe alternatives you've considered
For now I've been using a simple function I wrote to draw multiline text, in place of image.print. However, I really don't believe this should be necessary.

hi, could you write example to using this function please? big thanks

@kozmoz
Copy link
Contributor

kozmoz commented Jan 4, 2022

I also wrote a simple workaround to support newlines in texts. Works perfect for my left aligned texts.

Basically it makes use of the auto wrapping feature. It adds just enough spaces to the text to force a newline on the location where it has to break. Tested in NodeJS.

const Jimp = require('jimp');

const imageWidth = 1050;
const imageHeight = 330;
let text = 'First line \nsecond line \nlast line!';

new Jimp(imageWidth, imageHeight, '#FFFFFF', (err, image) => {
    Jimp.loadFont(Jimp.FONT_SANS_64_BLACK).then(font => {

        // Simulate newlines.
        const texts = text.split('\n');
        if (texts.length > 1) {
            for (let i = 0; i < texts.length - 1; i++) {
                let width = Jimp.measureText(font, texts[i] + '|');
                while (width < imageWidth) {
                    texts[i] += ' ';
                    width = Jimp.measureText(font, texts[i] + '|');
                }
            }
            text = texts.join('');
        }

        image.print(font, 0, 0, text, imageWidth, imageHeight);
        image.write(__dirname + '/image-with-text.png');
    });
});

Result:

image-with-text

@hipstersmoothie hipstersmoothie added help wanted enhancement a request for a new feature or change in behavior solution in issue labels Feb 6, 2023
@hipstersmoothie
Copy link
Collaborator

These all seem like great PRs

@kozmoz
Copy link
Contributor

kozmoz commented Oct 31, 2023

I'm afraid I have to delve into the code and create a patch to solve this issue myself. 😬

kozmoz pushed a commit to kozmoz/jimp that referenced this issue Nov 1, 2023
kozmoz pushed a commit to kozmoz/jimp that referenced this issue Nov 1, 2023
@kozmoz
Copy link
Contributor

kozmoz commented Nov 1, 2023

Created a Pull Request to solve this issue. PR #1265

Copy link
Contributor

🚀 Issue was released in v0.22.11 🚀

@github-actions github-actions bot added the released This issue/pull request has been released. label Feb 23, 2024
Copy link
Contributor

🚀 Issue was released in v0.22.11 🚀

@kozmoz
Copy link
Contributor

kozmoz commented Feb 23, 2024 via email

@hipstersmoothie
Copy link
Collaborator

Could you make an issue for me for that? Should be automatic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement a request for a new feature or change in behavior help wanted released This issue/pull request has been released. solution in issue
Projects
None yet
Development

No branches or pull requests

6 participants