Best way to render text on CMYK background? [SOLVED]

Magick.NET is an object-oriented C# interface to ImageMagick. Use this forum to discuss, make suggestions about, or report bugs concerning Magick.NET
Post Reply
bungle
Posts: 7
Joined: 2015-02-06T16:50:38-07:00
Authentication code: 6789

Best way to render text on CMYK background? [SOLVED]

Post by bungle » 2015-06-03T13:51:03-07:00

I am investigating adding customizable text to our application in which users compose selected items over a selected background. All of the source images are CMYK.

IM won't draw text on CMYK images, so I am first drawing the text onto a temporary RGB image and then converting the colorspace. I am using the correct ICC profiles and the color conversion seems accurate, and the output looks good. The problem is that when the colorspace is converted the source image seems to lose its transparency and gets a white background. Then I have to make the white area transparent before composing it onto the background. The problem is that I can't use white text because then it gets blanked as well. I also can't seem to control which color the transparency is converted to (so it's different than the white text). I can't find a way to keep the transparency on a colorspace conversion, which would also solve my problem. Am I hitting a known limitation or is there something else I can do?

Code: Select all

//Image is destination CMYK background image
MagickImage txtImage = new MagickImage(bgColor, curGeo.Width, curGeo.Height);
//font/color setup omitted
txtImage.Read("caption:" + text);
txtImage.AddProfile(Image.GetProfile(prof));
txtImage.ColorSpace = Image.ColorSpace;
//reinstate transparency
txtImage.Transparent(new ColorCMYK(0, 0, 0, 0, 1));
Image.Composite(txtImage, curGeo, CompositeOperator.SrcOver);
I attempted to get around this issue by drawing the text in black and white to use as a mask. The mask looks fine - the text is white with some gray around it for anti-aliasing. My understanding is that with a mask gray areas would fall between white and black in terms of what is applied. I can successfully use the mask with a tile operation to color the text area as desired, but it appears to ignore the gray, thus I lose anti-aliasing. Is there some option I'm missing or something else I can try? This is problem even with RGB.

Code: Select all

var black = new MagickColor(Color.Black);
var mi = new MagickImage(black, 500, 500);
mi.Font = "Arial";
mi.TextGravity = Gravity.Center;
mi.AntiAlias = true;
mi.FontPointsize = 64;
mi.FillColor = new MagickColor(Color.White);
mi.Annotate("My Mask", Gravity.Center);
mi.Write("text_mask.png"); //has anti-aliased white on black

var mi = new MagickImage(MagickColor.Transparent, 500, 500);
mi.Mask = new MagickImage("text_mask.png");
mi.Tile(new MagickImage("g-pink.png"), CompositeOperator.Blend);

mi.Write("text_maskfill.png");
One other issue I ran into is that it seems I can't use an image FillPattern unless I use Annotate, which means I lose word-wrapping, but I can probably work around that.

tl;dr - I'm trying to draw good-looking text of all colors on CMYK backgrounds. Please let me know if I'm barking up the wrong tree with my examples or I have in fact just met some limitations of IM.

EDIT: mark solved
Last edited by bungle on 2015-06-05T10:38:11-07:00, edited 1 time in total.

User avatar
fmw42
Posts: 25149
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: Best way to render text on CMYK background?

Post by fmw42 » 2015-06-03T14:47:19-07:00

I do not use Magick.Net, but have you tried saving the transparency layer, convert the image without the transparency to RGB, create your caption image, overlay it onto the RGB image, convert the result back to CMYK, then add the original transparency layer back to the final result. In command line it would be

unix syntax

Code: Select all

convert cmykimage \
\( -clone 0 -alpha extract \) \
\( -clone 0 -alpha off -profile .../sRGB.icc \) \
\( -size WxH xc:none -fill fillcolor caption:"your text" \) \
\( -clone 2 -clone 3 -geometry +Xoff+Yoff -compose over -composite -profile .../USWebCoatedSWOP.icc \) \
-delete 0,1,2 +swap -alpha off -compose copy_opacity newcmykimage

User avatar
dlemstra
Posts: 1562
Joined: 2013-05-04T15:28:54-07:00
Authentication code: 6789
Contact:

Re: Best way to render text on CMYK background?

Post by dlemstra » 2015-06-04T00:59:42-07:00

Can you add a link to your cmyk image? I would like to check why drawing on that file does not work.
.NET + ImageMagick = Magick.NET https://github.com/dlemstra/Magick.NET, @MagickNET, Donate

bungle
Posts: 7
Joined: 2015-02-06T16:50:38-07:00
Authentication code: 6789

Re: Best way to render text on CMYK background?

Post by bungle » 2015-06-04T14:00:47-07:00

I was just testing with a created image as in the boiled-down sample below.

Code: Select all

var mi = new MagickImage(MagickColor.Transparent, 500, 500);
mi.ColorSpace = ColorSpace.CMYK;
mi.Format = MagickFormat.Tiff;

MagickColor blue = ColorCMYK.FromMagickColor(new MagickColor(Color.Blue));
mi.Font = "Arial";
mi.FontPointsize = 32;
mi.FillColor = blue;
//converts to rgb!//mi.Read("caption:" + text);
mi.Annotate("annotation", Gravity.Center); //works

mi.Write("text_mc.tif");
I've played with this in so many different ways I guess I've lost track of what's what. So with respect to not being able to draw text on CMYK, it seems to only apply to calling txtImage.Read("caption:" + text); When I do that the image is converted to sRGB. However, if I use txtImage.Annotate(text) or Draw(drawables), then the image stays in CMYK, which is good! The only problem there is that neither of those word wrap, which is probably why most of my testing was with caption.

So perhaps I've already found my solution, I just need to solve word-wrapping, but that's probably not too difficult at least at a basic level. (This would be one- or two-liners, not documents.) Annotate looks good and also supports FillPattern, which is great.

In that case the last thing I'm having an issue is getting the right color. I know there are algorithms out there to convert but I thought these would be equivalent:
new MagickColor(255, 255, 0, 0, 255)
ColorCMYK.FromMagickColor(new MagickColor(Color.Blue)).

However, when using the latter the output has yellow text. Is there any way in Magick.NET to get an CMYK color from RGB in the same way that converting an image's colorspace does? Using ColorProfile? I've looked through the API and nothing jumps out at me. The client uses the USWebCoatedSWOP profile for the CMYK files.

User avatar
fmw42
Posts: 25149
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: Best way to render text on CMYK background?

Post by fmw42 » 2015-06-04T15:26:55-07:00

after doing txtImage.Read("caption:" + text);
just convert that to cmyk (using profiles) and composite it on your cmyk image

User avatar
dlemstra
Posts: 1562
Joined: 2013-05-04T15:28:54-07:00
Authentication code: 6789
Contact:

Re: Best way to render text on CMYK background?

Post by dlemstra » 2015-06-04T15:29:42-07:00

At this moment there is no color conversion available for a single color. What you could do is create a 1x1 sRGB image and convert it to cmyk to get the color. I could add this to Magick.NET. I will try to see if that works this weekend. Fred his suggestion is probably a better idea.
.NET + ImageMagick = Magick.NET https://github.com/dlemstra/Magick.NET, @MagickNET, Donate

bungle
Posts: 7
Joined: 2015-02-06T16:50:38-07:00
Authentication code: 6789

Re: Best way to render text on CMYK background?

Post by bungle » 2015-06-05T07:22:12-07:00

Converting a 1x1 image to get the color would work fine.

As for reading a caption to RGB and converting to CMYK, that gets back to the first problem I posted in the very first code box. When I do this I lose the transparency - the background turns to white. I can reinstate it as shown in the code sample (calling txtImage.Transparent), but the background always seems to be white after conversion, which means I can't draw white text because it's indistinguishable from the background. I don't know if I'm missing some setting or step to keep the transparency or if this is just inherent in the way things convert. Or maybe Fred's first post was already the solution? I haven't had time to look into that yet.

User avatar
fmw42
Posts: 25149
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: Best way to render text on CMYK background?

Post by fmw42 » 2015-06-05T08:59:28-07:00

Sorry I do not know the Magick.NET equivalent, but this works for me fine in command line:

Code: Select all

convert zelda3.jpg -profile ../images/profiles/USWebCoatedSWOP.icc zelda.tif
convert -size 216x -background none -gravity center -pointsize 24 -fill red \
	caption:"TESTING" -profile ../images/profiles/USWebCoatedSWOP.icc caption.tif
convert zelda.tif caption.tif -gravity center -compose over -composite zelda_caption_cmyk.tif
Note that one needs to use -background none rather than xc:none to keep the transparency, otherwise, the background is white.

bungle
Posts: 7
Joined: 2015-02-06T16:50:38-07:00
Authentication code: 6789

Re: Best way to render text on CMYK background?

Post by bungle » 2015-06-05T10:36:20-07:00

It seems that setting BackgroundColor to Transparent is the critical piece. I actually have that in my current code, so I tried it at some point. I ran so many tests I guess I wasn't doing a good enough job of controlling/tracking changes and thought the background was still collapsing to white. So I think what I was originally doing was the correct way. Sorry for the noise but thanks for helping me work through it!

Here's the final sample for history's sake, composing a couple of images and some text over a background. The text is properly overlaid.

Code: Select all

//all cmyk, same size
var bg = new MagickImage("bg.tif");
var hl = new MagickImage("headline.tif");
bg.Composite(hl, 0, 0, CompositeOperator.SrcOver);
var ps = new MagickImage("product.tif");
bg.Composite(ps, 0, 0, CompositeOperator.SrcOver);

var mi = new MagickImage(MagickColor.Transparent, 500, 500);
mi.Format = MagickFormat.Tiff;
mi.BackgroundColor = Color.Transparent; //critical piece
mi.Font = "Arial";
mi.FontPointsize = 120;
mi.TextGravity = Gravity.Center;
mi.AntiAlias = true;
mi.Read("caption:test caption");

var fillImg = new MagickImage("gradient-pink.png");
mi.FillPattern = fillImg;
mi.TextGravity = Gravity.Northwest;
mi.Annotate("Pink gradient", Gravity.Northwest);

mi.AddProfile(ColorProfile.USWebCoatedSWOP);
mi.ColorSpace = ColorSpace.CMYK;

var geo = new MagickGeometry(585, 1650, 500, 500);
bg.Composite(mi, geo, CompositeOperator.Over);
bg.Write("complete.tif");
It seems only Annotate looks at FillPattern, but I can live with that.

Post Reply