3

This command is to add a watermark to an image

magick nature.jpg -set option:watermarkWidth "%[fx:int(w*0.25)]" -alpha set -background none ( -fill "#FFFFFF80" -stroke "#FF000080" -strokeWidth 3 -undercolor "#FF000080" -size "%[watermarkWidth]x" label:"THIS IS WATERMARK" -gravity center -geometry +10+10 -rotate -30 ) -composite -quality 40 nature_wm.jpg

it can be run on Windows cmd window enter image description here

But it cannot be run in Powershell window(see the error screenshot) enter image description here

I also tried to add \ before ( and ) (which should do on macOS or Linux)

magick nature.jpg -set option:watermarkWidth "%[fx:int(w*0.25)]" -alpha set -background none \( -fill "#FFFFFF80" -stroke "#FF000080" -strokeWidth 3 -undercolor "#FF000080" -size "%[watermarkWidth]x" label:"THIS IS WATERMARK" -gravity center -geometry +10+10 -rotate -30 \) -composite -quality 40 nature_wm.jpg

But it throws the same error enter image description here

error translation: -fill: -fill can not be recognized as cmdlet, function, script file or runnable program. Please check the spelling of the name, and if you include a path, make sure the path is correct, then try again.

I'm sure that the magick directive is in the environment variable enter image description here

It seems that Powershell can run simple command but not complex command(the following command can be run, no error)

magick nature.jpg -fill yellow nature.png

enter image description here

Anyone who knows how to solve this problem?

Actually I need to run it with golang, by using cmd = exec.Command("cmd", "/k", cmdStr), but it doesn't work if the "cmdStr" cannot be run in Powershell, because the executable the I build with golang need to run on Powershell too(I don't want to run on cmd window, because it's so primitive compared to Powershell).

Willis
  • 599
  • 3
  • 25
  • What is the result if it is run in `cmd` and not in `powershell`? – lit Apr 01 '22 at 12:40
  • @lit Damn, it works....but why it doesn't work in powershell? how can I make it run in powershell window? – Willis Apr 01 '22 at 12:54
  • An easy workaround would be to put the command in a .bat file script and run it from PowerShell. – lit Apr 01 '22 at 13:09
  • @lit But I need to run the command in golang – Willis Apr 01 '22 at 13:17
  • What if you run `magick nature.jpg -fill yellow nature.png` – Mark Setchell Apr 01 '22 at 14:16
  • I removed `batch-file` and added the `go` tag. I am not sure how `powershell` is related to this situation. Please do some searching on magick and go. – lit Apr 01 '22 at 14:20
  • @MarkSetchell No error, but the image seems no change at all – Willis Apr 01 '22 at 14:33
  • 1
    So I think your assertion that the `-fill` causes problems might be wrong. It is probably the opening parenthesis. Try putting a backtick immediately in front of the opening and closing parentheses. – Mark Setchell Apr 01 '22 at 14:39
  • @MarkSetchell I tried, same error, as if I didn't add that backslash `\` at all. – Willis Apr 01 '22 at 14:53
  • 2
    Not backslash, but backtick https://www.computerhope.com/jargon/b/backquot.htm – Mark Setchell Apr 01 '22 at 14:56
  • I don't expect the command I suggested to actually *do* anything. I just want to be sure it doesn't cause an error. When it doesn't cause an error, we can fix your actual command. – Mark Setchell Apr 01 '22 at 14:57
  • @MarkSetchell Wow, it works, so on Windows, backslash can not escape a character, instead, backstick did. – Willis Apr 01 '22 at 15:03
  • 3
    Nearly! On Powershell, parentheses require escaping and Powershell's escape character is the backtick. On CMD, parentheses do not require escaping, but if they did, the escape character is the circumflex (`^`). – Mark Setchell Apr 01 '22 at 15:09
  • @MarkSetchell Really appreciate for your detailed explanation, problem solved. – Willis Apr 01 '22 at 15:14
  • I'll try to write up an answer with some examples later. – Mark Setchell Apr 01 '22 at 15:17

2 Answers2

3

TL;DR

There's a very simple, quoting-independent, cross-platform way of writing ImageMagick scripts given at the end of this post...


On quotes and quoting and escapes and escaping ImageMagick commands in bash, CMD32 and Powershell

As a result of its sheer versatility and power, ImageMagick offers a far richer palette of options, switches and parameters than most command-line programs (around 300 options and switches are listed here) and it uses characters and symbols to allow users to express things in a very natural way. As a result, some degree of caution is required when using ImageMagick in the many environments in which it can run, namely:

  • under bash or other Unix/Linux shells such as zsh, ksh, tcsh,
  • under Windows CMD32 and in Windows BATCH files,
  • under Powershell
  • under things like MinGW, MSYS, Cygwin

For example, in and of itself, disregarding the shell, ImageMagick understands the following:

  • # or hash, a.k.a. pound sign is used to express hexadecimal colours, in a natural way, like -fill #ff0000 for a red fill colour. However, bash interprets that same character as introducing a comment, so in bash you would normally write -fill '#ff0000'

  • () or parentheses. ImageMagick uses parentheses to apply processing to a specific image in its stack, also called "aside processing". So, this command loads two images and resizes both magick IMAGE1.PNG IMAGE2.PNG -resize 800x600 ... but if you want to resize only the second, you would do magick IMAGE1.PNG ( IMAGE2.PNG -resize 800x600 ) ... However, bash uses parentheses for sub-processes, so it will think you want to run a sub-process called IMAGE2.PNG unless you escape the parentheses with magick IMAGE1.PNG \( IMAGE2.PNG -resize 800x600 \) .... Likewise, Powershell will object to the parentheses, and you must put a backtick before opening and closing parentheses. CMD32 doesn't treat parentheses as special at all, so they need no escaping in that environment.

  • () or parentheses. ImageMagick uses these to introduce hex or hsl colours, e.g. -fill RGBA(255,0,0) or -fill hsl(50,60,70). Again, bash will dislike that, thinking you want a sub-shell, so folks write -fill "rgb(255,0,0)". I assume Powershell will not like it either.

  • % or percent. ImageMagick commonly uses that, and similar, to resize an image to 50% of its original size -resize 50%. But, if you use percent signs in a Windows BATCH file, you need to double them up else it thinks you are referring to its command-line parameters.

  • < and >, or less than and greater than. ImageMagick uses > to mean you only want a resize applied to images greater than a certain size, e.g. -resize 800> which means you only want images larger than 800 scaled down to 800, but you don't want images under 800 scaled up. Likewise with <. However, bash uses those characters for redirection of input and output, so in bash you would normally write -resize '800>' Likewise in CMD32, you will need to escape both < and > by preceding with a caret ^. And precede with backticks in Powershell.

  • ! or exclamation mark, a.k.a. "bang". ImageMagick uses that to mean "Just do it!". So, for example, -resize 800x600 says you want to resize such that the width doesn't exceed 800 and the height doesn't exceed 600 and the aspect ratio should be respected. However, when you add the bang like this -resize 800x600! you will get exactly 800x600 pixels even if that distorts your image horribly. The shell can interpret the exclamation as introducing some manipulation of its history of previous commands, so you will often see that escaped

  • [ and ], or square brackets. ImageMagick uses these to refer to a page or subset of pages in a multipage document such as a PDF or a TIFF. For example, the following means the first and last page of a PDF, magick DOCUMENT.PDF[0,-1] ... That can get confused with shell syntax for an alternation if you don't handle it with care

  • * or asterisk. ImageMagick understands the asterisk as a globbing character to expand the list of all matching files, e.g. *.tif meaning all the TIFF files in the current directory. That is the same as bash, but there are nuances here. If you use magick *.tif ..., the list of TIFFs will be expanded by your shell in bash and that will be subject to your system's ARGMAX. But if you do magick '*.TIF' ... it will be expanded internally by ImageMagick and not be subject to such limits.


General hints for bash and Unix/Linux shells

  • The line continuation character is the backslash

  • Opening and closing parentheses must be escaped with backslashes immediately in front of them

  • Hashes must be within double or single quoted strings

Example of bash command

magick IMAGE1.PNG \
   \( IMAGE2.PNG -resize 50% -fill '#ff0000' -colorize 100% \) \
  -composite -transparent 'hsl(40,50,60)' result.png

General hints for Windows CMD32

  • the caret ^ is used as the escape character

  • CMD32 generally dislikes any usage of single quotes. Generally, if translating from a bash incantation of ImageMagick, try replacing single quotes with double quotes. The exception to this is when already inside double quotes, where you can use single quotes, e.g. -draw "text 100,100 'Works like magick'"

  • the caret is used as the line continuation character and may not be followed by any spaces when used that way

  • percent signs must be doubled up in BATCH

  • parentheses do not require escaping

Example of Windows CMD32 BATCH command

magick IMAGE1.PNG ^
   ( IMAGE2.PNG -resize 50%% -fill "#ff0000" -colorize 100% ) ^
  -composite -transparent "hsl(40,50,60)" result.png

General hints for Powershell

  • the backtick is used as the escape character, and the line continuation character
  • opening and closing parentheses must be escaped with a preceding backtick

Example of Powershell command

magick IMAGE1.PNG `
   `( IMAGE2.PNG -resize 50% -fill "#ff0000" -colorize 100% `) `
  -composite -transparent "hsl(40,50,60)" result.png

TL;DR

If you want a cross-platform, or platform independent way of writing ImageMagick scripts, the easiest is to put all the commands in a file whose contents are only read by ImageMagick itself rather than being dependent on your shell. So, write a script like this, which is pure and has no quoting, and save it as script.mgk:

-size 640x480 xc:#ffff00
( foreground.png -resize 50% )
-gravity center -composite -write result.png

Then invoke it with:

magick -script script.mgk

and your shell, whatever that may be, doesn't even see the quotes, percent signs, hash signs, line-continuations or parentheses:

Compare and contrast with bash where you would need:

magick -size 640x480 xc:'#ffff00'    \
   \( foreground.png -resize 50% \)  \
   -gravity centre -composite result.png

and Windows CMD32.EXE where you would need:

magick -size 640x480 xc:#ffff00     ^
   ( foreground.png -resize 50%% )  ^
   -gravity centre -composite result.png    
Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
3

When testing EXE files in PowerShell, EchoArgs.exe is a very useful tool to have on hand. The original source for this tool appears to have disappeared, but you can still download it from ss64.com on this page.

When dealing with complex command lines for EXE files, Stop-parsing token (--%) is a very useful special parsing token to keep in mind.

In the command line execute EchoArgs with your parameters:

EchoArgs nature.jpg -set option:watermarkWidth "%[fx:int(w*0.25)]" -alpha set -background none ( -fill "#FFFFFF80" -stroke "#FF000080" -strokeWidth 3 -undercolor "#FF000080" -size "%[watermarkWidth]x" label:"THIS IS WATERMARK" -gravity center -geometry +10+10 -rotate -30 ) -composite -quality 40 nature_wm.jpg

Record your results:

Arg 0 is <nature.jpg>
Arg 1 is <-set>
Arg 2 is <option:watermarkWidth>
Arg 3 is <%[fx:int(w*0.25)]>
Arg 4 is <-alpha>
Arg 5 is <set>
Arg 6 is <-background>
Arg 7 is <none>
Arg 8 is <(>
Arg 9 is <-fill>
Arg 10 is <#FFFFFF80>
Arg 11 is <-stroke>
Arg 12 is <#FF000080>
Arg 13 is <-strokeWidth>
Arg 14 is <3>
Arg 15 is <-undercolor>
Arg 16 is <#FF000080>
Arg 17 is <-size>
Arg 18 is <%[watermarkWidth]x>
Arg 19 is <label:THIS IS WATERMARK>
Arg 20 is <-gravity>
Arg 21 is <center>
Arg 22 is <-geometry>
Arg 23 is <+10+10>
Arg 24 is <-rotate>
Arg 25 is <-30>
Arg 26 is <)>
Arg 27 is <-composite>
Arg 28 is <-quality>
Arg 29 is <40>
Arg 30 is <nature_wm.jpg>

To avoid problem, use --% in PowerShell at the point where the command line becomes complex:

EchoArgs nature.jpg -set option:watermarkWidth --% "%[fx:int(w*0.25)]" -alpha set -background none ( -fill "#FFFFFF80" -stroke "#FF000080" -strokeWidth 3 -undercolor "#FF000080" -size "%[watermarkWidth]x" label:"THIS IS WATERMARK" -gravity center -geometry +10+10 -rotate -30 ) -composite -quality 40 nature_wm.jpg

Check your results:

Arg 0 is <nature.jpg>
Arg 1 is <-set>
Arg 2 is <option:watermarkWidth>
Arg 3 is <%[fx:int(w*0.25)]>
Arg 4 is <-alpha>
Arg 5 is <set>
Arg 6 is <-background>
Arg 7 is <none>
Arg 8 is <(>
Arg 9 is <-fill>
Arg 10 is <#FFFFFF80>
Arg 11 is <-stroke>
Arg 12 is <#FF000080>
Arg 13 is <-strokeWidth>
Arg 14 is <3>
Arg 15 is <-undercolor>
Arg 16 is <#FF000080>
Arg 17 is <-size>
Arg 18 is <%[watermarkWidth]x>
Arg 19 is <label:THIS IS WATERMARK>
Arg 20 is <-gravity>
Arg 21 is <center>
Arg 22 is <-geometry>
Arg 23 is <+10+10>
Arg 24 is <-rotate>
Arg 25 is <-30>
Arg 26 is <)>
Arg 27 is <-composite>
Arg 28 is <-quality>
Arg 29 is <40>
Arg 30 is <nature_wm.jpg>

But what if you need to dynamically replace part of the parameters after the the Stop-parsing token (--%)? That is doable via environmental variables:

$Env:FirstValue = '%[fx:int(w*0.25)]'
$Env:Alpha = 'set'
$Env:Background = 'none'
$Env:Fill = '#FFFFFF80'
$Env:Stroke = '#FF000080'
$Env:StrokeWidth = '3'
$Env:Undercolor = '#FF000080'
$Env:Size =  '%[watermarkWidth]x'
$Env:Label = 'THIS IS WATERMARK'
$Env:Gravity = 'center'
$Env:Geometry = '+10+10'
$Env:Rotate = '-30'
$Env:Quality = '40'
$Env:ImgName = 'nature_wm.jpg'
EchoArgs nature.jpg -set option:watermarkWidth --% "%FirstValue%" -alpha %Alpha% -background %Background% ( -fill "%Fill%" -stroke "%Stroke%" -strokeWidth %StrokeWidth% -undercolor "%Undercolor%" -size "%[watermarkWidth]x" label:"%Label%" -gravity %Gravity% -geometry %Geometry% -rotate %Rotate% ) -composite -quality %Quality% %ImgName%

Again, check your results:

Arg 0 is <nature.jpg>
Arg 1 is <-set>
Arg 2 is <option:watermarkWidth>   
Arg 3 is <%[fx:int(w*0.25)]>       
Arg 4 is <-alpha>
Arg 5 is <set>
Arg 6 is <-background>
Arg 7 is <none>
Arg 8 is <(>
Arg 9 is <-fill>
Arg 10 is <#FFFFFF80>
Arg 11 is <-stroke>
Arg 12 is <#FF000080>
Arg 13 is <-strokeWidth>
Arg 14 is <3>
Arg 15 is <-undercolor>
Arg 16 is <#FF000080>
Arg 17 is <-size>
Arg 18 is <%[watermarkWidth]x>     
Arg 19 is <label:THIS IS WATERMARK>
Arg 20 is <-gravity>
Arg 21 is <center>
Arg 22 is <-geometry>
Arg 23 is <+10+10>
Arg 24 is <-rotate>
Arg 25 is <-30>
Arg 26 is <)>
Arg 27 is <-composite>
Arg 28 is <-quality>
Arg 29 is <40>
Arg 30 is <nature_wm.jpg>

But what if you don't know where the EXE is and have to search for it on the hard drive and call it from a variable? Use The call operator (&):

$Program = 'C:\Program Files\ImageMagick-7.0.11-Q16-HDRI\magick.exe'
$Env:FirstValue = '%[fx:int(w*0.25)]'
$Env:Alpha = 'set'
$Env:Background = 'none'
$Env:Fill = '#FFFFFF80'
$Env:Stroke = '#FF000080'
$Env:StrokeWidth = '3'
$Env:Undercolor = '#FF000080'
$Env:Size =  '%[watermarkWidth]x'
$Env:Label = 'THIS IS WATERMARK'
$Env:Gravity = 'center'
$Env:Geometry = '+10+10'
$Env:Rotate = '-30'
$Env:Quality = '40'
$Env:ImgName = 'nature_wm.jpg'
& $Program nature.jpg -set option:watermarkWidth --% "%FirstValue%" -alpha %Alpha% -background %Background% ( -fill "%Fill%" -stroke "%Stroke%" -strokeWidth %StrokeWidth% -undercolor "%Undercolor%" -size "%[watermarkWidth]x" label:"%Label%" -gravity %Gravity% -geometry %Geometry% -rotate %Rotate% ) -composite -quality %Quality% %ImgName%

Now, having said all that, the real test is to actually execute the command and see if it works. I don't have your image file and not really up to doing any experiments with Image Magick. But, in theory, this command should work work for you:

magick nature.jpg -set option:watermarkWidth --% "%[fx:int(w*0.25)]" -alpha set -background none ( -fill "#FFFFFF80" -stroke "#FF000080" -strokeWidth 3 -undercolor "#FF000080" -size "%[watermarkWidth]x" label:"THIS IS WATERMARK" -gravity center -geometry +10+10 -rotate -30 ) -composite -quality 40 nature_wm.jpg

EDIT:

If you ware wanting MagicK to run in GoLang, use the following code:

package main

import (
    "fmt"
    "os/exec"
)


func main() {
    data, err := exec.Command("magick", "nature.jpg", "-set", "option:watermarkWidth", "%[fx:int(w*0.25)]", "-alpha", "set", "-background", "none", "(", "-fill", "#FFFFFF80", "-stroke", "#FF000080", "-strokeWidth", "3", "-undercolor", "#FF000080", "-size", "%[watermarkWidth]x", "label:THIS IS WATERMARK", "-gravity", "center", "-geometry", "+10+10", "-rotate", "-30", ")", "-composite", "-quality", "40", "nature_wm.jpg").Output()
    if err != nil {
        panic(err)
    }
    fmt.Println(string(data))
}
Darin
  • 1,423
  • 1
  • 10
  • 12
  • I don't familiar with windows commands but it did work after adding `--%`, thank you. – Willis Apr 04 '22 at 09:30
  • @Willis, what happened when you ran the command? I just now tried it in PowerShell and it worked for me. Exact same result in PowerShell as in CMD. A new image was created with a watermark going diagonal across the image. – Darin Apr 04 '22 at 13:32
  • @Willis, I've added GoLang code from running MagicK. – Darin Apr 04 '22 at 22:01