[Solved] Specifying a maximum width for long caption text while for shorter strings occupying a shorter width?

Questions and postings pertaining to the usage of ImageMagick regardless of the interface. This includes the command-line utilities, as well as the C and C++ APIs. Usage questions are like "How do I use ImageMagick to create drop shadows?".
Post Reply
cake
Posts: 10
Joined: 2019-09-16T02:39:51+00:00
Authentication code: 1152

[Solved] Specifying a maximum width for long caption text while for shorter strings occupying a shorter width?

Post by cake »

Following on from my previous topic about an existing label setup I then began looking into 'caption' which shares the same syntax as 'label' but allows for automatic text wrapping when paired with the -size option.

The goal I had was to define a maximum width for a caption to wrap text at, while for text strings shorter than that width (ie: strings that only require a single line) for the width dimension to match that of the text string instead of being the fixed width.

For example as a brief snippet, adjusted from code of the linked previous topic, I can create a caption width of 150px:

Code: Select all

-size 150x caption:"My text string here"
Image

And with a longer string for comparison, showing the wrapping:

Image

Is there a way to avoid the additional whitespace for single line text strings shorter than the maximum width specified, that is font independent? As my assumption is that glyph widths would differ between fonts so I'm not sure that calculating string length alone would work.

Goal for shorter strings (note: the red seen is just the background of the image I had beneath for the example screenshots):

Image
Last edited by cake on 2019-10-10T04:38:32+00:00, edited 1 time in total.
snibgo
Posts: 12159
Joined: 2010-01-24T06:01:33+00:00
Authentication code: 1151
Location: England, UK

Re: Specifying a maximum width for long caption text while for shorter strings occupying a shorter width?

Post by snibgo »

What version of IM, on what platform?

I suppose you are specifying pointsize. Please always show complete commands, or we need to guess.

You can test the height of a result, to see whether the text is on a single line. If it is, you can re-run the command with no specified width.
snibgo's IM pages: im.snibgo.com
cake
Posts: 10
Joined: 2019-09-16T02:39:51+00:00
Authentication code: 1152

Re: Specifying a maximum width for long caption text while for shorter strings occupying a shorter width?

Post by cake »

snibgo wrote: 2019-10-02T11:14:04+00:00What version of IM, on what platform? I suppose you are specifying pointsize. Please always show complete commands, or we need to guess.
IM 7.0.7 on Windows. The full label code I'm using is that from the previous topic I linked in the OP (which you graciously helped with, thanks again!) but here just showing the two changes made to convert it to a 'caption' instead of a 'label'.

snibgo wrote: 2019-10-02T11:14:04+00:00You can test the height of a result, to see whether the text is on a single line. If it is, you can re-run the command with no specified width.

Hmm, interesting idea, is it possible to somehow check output dimensions within IM but without creating a temp file? I wonder if IM's base64 output would be small enough to work with the character limit in cmd variables but perhaps there's another way.

As since the pointsize and font choice is subject to change arbitrarily using settings I'd have to perform a temp test for how high a single line is first, then run a test for each other label created like you mentioned to check whether the height exceeds that.
snibgo
Posts: 12159
Joined: 2010-01-24T06:01:33+00:00
Authentication code: 1151
Location: England, UK

Re: Specifying a maximum width for long caption text while for shorter strings occupying a shorter width?

Post by snibgo »

If the caption image is a single line, you want to trim it. Correct?

How about this (Windows BAT syntax):

Code: Select all

set MYCAPT=My much xxxxxxx longer string
set MYCAPT=My string

magick ^
  -pointsize 15 ^
  ( caption:"MIyg" ) ^
  ( -size 150x caption:"%MYCAPT%" ) ^
  -set option:DELWHICH "%%[fx:u.h<v.h?1:0]\n" ^
  -delete 0 ^
  ( +clone -trim ) ^
  -delete %%[DELWHICH] ^
  x.png
We create a short caption image to get the height of a single line, then the real caption image. We compare the heights, seting DELWHICH to 1 if the "real" heights is larger or the same. We remove the single-line image and create another, a trimmed version of the real image. We then remove one of these images. The result is what you want.
snibgo's IM pages: im.snibgo.com
cake
Posts: 10
Joined: 2019-09-16T02:39:51+00:00
Authentication code: 1152

Re: Specifying a maximum width for long caption text while for shorter strings occupying a shorter width?

Post by cake »

snibgo wrote: 2019-10-02T12:48:03+00:00If the caption image is a single line, you want to trim it. Correct?
Kind of. Before you suggested -trim I was thinking I'd have to set a batch variable (eg: text-single-line=1) which would conditionally change -caption to -label instead, if a string wasn't longer than the maximum width (ie: a single line). That way it would be a 'caption' if longer than the -size width value and changed to -label if shorter than that width.
snibgo wrote: 2019-10-02T12:48:03+00:00How about this (Windows BAT syntax)

We create a short caption image to get the height of a single line, then the real caption image. We compare the heights, seting DELWHICH to 1 if the "real" heights is larger or the same. We remove the single-line image and create another, a trimmed version of the real image. We then remove one of these images. The result is what you want.
Sounds promising though the issue I'm seeing when testing trim sadly is that the little extra of whitespace around the characters it removes after it's applied affects the alignment of the text differently in the final output than the non-trimmed version. Example (upper: your sample, below: the IM commands from the previous topic with the height test added):

Image

The batch script for testing this (the rem before the set MYCAPT line comments out the line, can be removed to enable that particular variable):

Code: Select all

@echo off
setlocal enabledelayedexpansion

rem set MYCAPT=My much longer string zzlorem ipsum etc and so on and so forth
set MYCAPT=My much

magick ^
  -background rgba(51,153,255,0.7) -fill rgba(0,0,0,1) -font "Segoe-UI-Semibold" -pointsize "15" ^
  ( caption:"MIyg" ) ^
  ( -size 150x caption:"%MYCAPT%" ) ^
  -set option:DELWHICH "%%[fx:u.h<v.h?1:0]\n" ^
  -delete 0 ^
  ( +clone -trim ) ^
  -delete %%[DELWHICH] ^
  -gravity southeast -splice 6x4 ^
  -gravity northwest -splice 6x2 ^
  ^( +clone ^
     -alpha extract ^
     +write mpr:ALP ^
     +delete ^
  ^) ^
  ^( +clone ^
     -alpha off ^
     -fill White -colorize 100 ^
     -draw " fill black polygon 0,0 0,2 2,0 fill white circle 2,2 2,0" ^
      ^( +clone -flip ^) -compose Multiply -composite ^
      ^( +clone -flop ^) -compose Multiply -composite ^
     mpr:ALP ^
     -compose Multiply -composite ^
  ^) ^
  -alpha off ^
  -compose CopyOpacity -composite ^
  output-test.png

pause
endlocal
Perhaps there's a way to solve the trim whitespace issue? Otherwise is there a way this IM height check could be adapted to conditionally change the -caption to a -label if it detects the text is a single line? As being a 'label' ignores the 'size' width which would be ideal, since so far the commands don't require creating a temp file.
snibgo
Posts: 12159
Joined: 2010-01-24T06:01:33+00:00
Authentication code: 1151
Location: England, UK

Re: Specifying a maximum width for long caption text while for shorter strings occupying a shorter width?

Post by snibgo »

The trim line can be more complex, eg adding a border:

Code: Select all

  ( +clone -trim +repage -bordercolor rgba(51,153,255,0.7) -compose Copy -border 2 +compose ) ^
We can't have a single command that uses either "label:" or "caption:" according to some condition. However, we would have a command that uses both then deletes one according to some condition.
snibgo's IM pages: im.snibgo.com
cake
Posts: 10
Joined: 2019-09-16T02:39:51+00:00
Authentication code: 1152

Re: Specifying a maximum width for long caption text while for shorter strings occupying a shorter width?

Post by cake »

snibgo wrote: 2019-10-02T15:18:32+00:00The trim line can be more complex, eg adding a border
Tested and unfortunately appears even with something like -border 0x2 (affecting just the top/bottom) to not be able to match the whitespace/alignment of the original.
snibgo wrote: 2019-10-02T15:18:32+00:00We can't have a single command that uses either "label:" or "caption:" according to some condition. However, we would have a command that uses both then deletes one according to some condition.
So where both -label and -caption would be defined simultaneously and then one conditionally removed? If that were possible it'd be great. Even if the entire styling command part were necessary to be duplicated (one with label and one with caption) it wouldn't matter in my case since the values would all be defined by external variables anyway.
cake
Posts: 10
Joined: 2019-09-16T02:39:51+00:00
Authentication code: 1152

Re: Specifying a maximum width for long caption text while for shorter strings occupying a shorter width?

Post by cake »

Figured out an alternative in the meantime (server also seemed to be having HTTPS certificate issues when I checked a few times).

The following is a for loop that checks the dimensions of the single line test string against the actual string, as per snibgo's example, then displays the IM info which is text that is subsequently parsed to extract just the height value from. All without temp files.

Note that spaces from the actual string are replaced with underscores so the dimensions checked by the tokens in the inner for loop are always in the same position (otherwise it would think any spaces in the text are delimiters).

Code: Select all

set "cnt=0"
for /f "usebackq delims=" %%a in (`magick -pointsize 15 ^( caption:"MIyg" ^) ^( -size 150x caption:"!MYCAPT: =_!" ^) info:`) do (
    set /a "cnt+=1"
    for /f "tokens=4 delims=x " %%h in ("%%a") do set "texth[!cnt!]=%%h"
    )
if !texth[1]! neq !texth[2]! (
    set "texttype=-size 150x caption:"
    ) else (
    set "texttype=label:"
    )
The texttype variable can then be added to the full IM command right before the text string and will change depending on whether the text is less than the max width. While testing I did in fact find that size does affect label but only if positioned after it (IIRC) so I conditionally removed it entirely as seen in the above code.

If there's a simpler way to achieve this though I'm all ears :D
cake
Posts: 10
Joined: 2019-09-16T02:39:51+00:00
Authentication code: 1152

Re: Specifying a maximum width for long caption text while for shorter strings occupying a shorter width?

Post by cake »

Just another thing: is it possible to somehow obtain info: output from two different points in the one command? Or some way to store two then output one at the end of the same command?

I'd like to be able to check the dimensions result of a command sequence using info: and then check the -format "%@" (calculated -trim values) output after the same command sequence. Is this achievable?

Or alternatively if there were a way of outputting as text something that includes the dimensions and the calculated trim values (seen when using -format "%@") using some other command.
snibgo
Posts: 12159
Joined: 2010-01-24T06:01:33+00:00
Authentication code: 1151
Location: England, UK

Re: Specifying a maximum width for long caption text while for shorter strings occupying a shorter width?

Post by snibgo »

You can insert "+write info:" at any point in the command. Another hint: insert "+write abc.png" anywhere. Very handy for debugging.

The format can contain other characters, so we can do things like this:

Code: Select all

magick rose: -format FIRST=%@\n +write info: -border 10 -format SECOND=%@\n info:

FIRST=70x46+0+0
SECOND=70x46+10+10
snibgo's IM pages: im.snibgo.com
cake
Posts: 10
Joined: 2019-09-16T02:39:51+00:00
Authentication code: 1152

Re: Specifying a maximum width for long caption text while for shorter strings occupying a shorter width?

Post by cake »

snibgo wrote: 2019-10-09T10:55:54+00:00 You can insert "+write info:" at any point in the command. Another hint: insert "+write abc.png" anywhere. Very handy for debugging.

The format can contain other characters, so we can do things like this
Fantastic. I also realized I could use -format "dimens=%w %h" (double percents required for escaping in batch, obviously) to grab the dimensions more directly without the extraneous info. Very neat tip, thank you.
Post Reply