Photoshop: CMYK ink density gamut warning

  • 5
  • Idea
  • Updated 5 years ago
  • (Edited)
This is a request from those that do CMYK prepress work in Photoshop.
We are often having to convert images to CMYK profiles with custom ink densities to match certain publication standards.
There are profiled ink densities of all four inks that equal
240
280
340
etc...

The only way to check out the work we do in Photoshop is to generate a PDF of our file, Open it in Acrobat,
Go to Tools,
Output Preview
and check the Total Area Coverage warning.

It is Way too many extra steps for something that needs to get possibly re-adjusted in Photoshop.

It would be amazing to have this sort of preview within Photoshop. It would enhance productivity and file quality.
Unfortunately, I don't think there are too many pre-press operators that visit this forum but i can say that the prepress community is massive and would benefit greatly from this.
See the attached image of what I have to do in Acrobat.

Photo of joshua withers

joshua withers

  • 84 Posts
  • 16 Reply Likes
  • in need of this feature

Posted 5 years ago

  • 5
Photo of christoph pfaffenbichler

christoph pfaffenbichler, Champion

  • 1208 Posts
  • 166 Reply Likes
One can automate a way of indicating areas above of a certain TAC in CMYK images, but there is a certain imprecission that gets worse if Spot Channels are involved.
Unfortunately the result, while it will reflect changes performed on the underlying image, would print if one does not hide/delete it before final saving.

Edit: If no Spot Channels are involved two Adjustment Layers suffice, but as the four Channels are bunched up into one the result is not perfect.
Also changes in the Spot Channels are not reflected.
Photo of joshua withers

joshua withers

  • 84 Posts
  • 16 Reply Likes
Humm.
Looks like a nice work around.
What is that ink density layer with the curve clipped to it?
Would you mind sharing it?

Thank you for your reply.

-josh
Photo of christoph pfaffenbichler

christoph pfaffenbichler, Champion

  • 1208 Posts
  • 166 Reply Likes
The lower Adjustment Layer is a Channel Mixer that »empties« Cyan, Yellow and Black and makes Magenta a mixture of 25% of each of the four Channels and has a Blend if »This Layer« setting corresponding to the intended TAC.
The Curves Layer adjusts the Layer additionally ...
But basically I use below Script to generate it, feel free to give it a try and let me know if you notice problems.

/* creates layers that mark areas that more or less exceed a certain total area coverage;
the more channels the more pronounced the rounding issues;
based on a discussion with j maloney;
2013, use it at your own risk */
if (app.documents.length > 0 && app.activeDocument.mode == DocumentMode.CMYK) {
/* dialog;*/
var dlg = new Window("dialog", "indicate tac of and over", [500,300,750,420]);
/* filter for checking if entry is numeric, thanks to xbytor;*/
numberKeystrokeFilter = function() {
if (this.text.match(/[^\-\.\d]/)) {
this.text = this.text.replace(/[^\d]/g, "")
};
if (this.text 500) {this.text = "500"}
};
/* field for entry;*/
dlg.tac = dlg.add("edittext", [15,15,110,35], "300", {multiline:false});
dlg.tac.active = true;
dlg.tac.onChange = numberKeystrokeFilter;
/* warning;*/
dlg.warning = dlg.add("statictext", [13,78,240,150], "please remember to remove or");
dlg.warning2 = dlg.add("statictext", [13,95,240,150], "hide the indicator layers");
/* ok- and cancel-button;*/
dlg.buildBtn = dlg.add("button", [13,45,118,68], "OK", {name:"ok"});
dlg.cancelBtn = dlg.add("button", [128,45,240,68], "Cancel", {name:"cancel"});
/* show dialog;*/
dlg.center();
/* show dialog;*/
var myReturn = dlg.show ();
if (myReturn == true) {
var myDocument = app.activeDocument;
/* get the number of tac;*/
var total = Number (dlg.tac.text);
var myDocument = app.activeDocument;
/* go to top layer;*/
var theVis = myDocument.layers[0].visible;
myDocument.activeLayer = myDocument.layers[0];
if (myDocument.layers[0].iBackgroundLayer == false) {myDocument.layers[0].visible = theVis};
/* set to composite;*/
app.activeDocument.activeChannels = [app.activeDocument.channels[0], app.activeDocument.channels[1], app.activeDocument.channels[2], app.activeDocument.channels[3]];
/* array for printing channels;*/
var theChannels = [0, 1, 2, 3];
/* check for spot colors;*/
for (var m = myDocument.channels.length - 1; m >= 4 ; m--) {
if (myDocument.channels[m].kind == ChannelType.SPOTCOLOR) {
theChannels = theChannels.concat(m)
}
};
/* create channel mixer;*/
var channelMixer = theChannelMix(theChannels.length);
channelMixer.name = "total area coverage "+total+"%";
/* work throuh spot channels;*/
for (var n = theChannels.length - 1; n >= 4 ; n--) {
myDocument.selection.load(myDocument.channels[theChannels[n]]);
/* make solid color layer;*/
var idMk = charIDToTypeID( "Mk " );
var desc72 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref57 = new ActionReference();
var idcontentLayer = stringIDToTypeID( "contentLayer" );
ref57.putClass( idcontentLayer );
desc72.putReference( idnull, ref57 );
var idUsng = charIDToTypeID( "Usng" );
var desc73 = new ActionDescriptor();
var idType = charIDToTypeID( "Type" );
var desc74 = new ActionDescriptor();
var idClr = charIDToTypeID( "Clr " );
var desc75 = new ActionDescriptor();
var idCyn = charIDToTypeID( "Cyn " );
desc75.putDouble( idCyn, 0.000000 );
var idMgnt = charIDToTypeID( "Mgnt" );
desc75.putDouble( idMgnt, 100.000000 );
var idYlw = charIDToTypeID( "Ylw " );
desc75.putDouble( idYlw, 0.000000 );
var idBlck = charIDToTypeID( "Blck" );
desc75.putDouble( idBlck, 0.000000 );
var idCMYC = charIDToTypeID( "CMYC" );
desc74.putObject( idClr, idCMYC, desc75 );
var idsolidColorLayer = stringIDToTypeID( "solidColorLayer" );
desc73.putObject( idType, idsolidColorLayer, desc74 );
var idcontentLayer = stringIDToTypeID( "contentLayer" );
desc72.putObject( idUsng, idcontentLayer, desc73 );
executeAction( idMk, desc72, DialogModes.NO );
myDocument.activeLayer.name = myDocument.channels[theChannels[n]].name;
myDocument.activeLayer.opacity = Math.ceil(100 / theChannels.length);
myDocument.activeLayer.grouped = true;
myDocument.channels[theChannels[n]].visible = false;
};
/* create curves layer;*/
var curvesLayer = theCurvesMix (total, theChannels.length);
}
};
/* function for channel mixer */
function theChannelMix (channelNumber) {
var thePerc = Math.ceil(100 / channelNumber);
var idMk = charIDToTypeID( "Mk " );
var desc3 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref2 = new ActionReference();
var idAdjL = charIDToTypeID( "AdjL" );
ref2.putClass( idAdjL );
desc3.putReference( idnull, ref2 );
var idUsng = charIDToTypeID( "Usng" );
var desc4 = new ActionDescriptor();
var idType = charIDToTypeID( "Type" );
var desc5 = new ActionDescriptor();
var idpresetKind = stringIDToTypeID( "presetKind" );
var idpresetKindType = stringIDToTypeID( "presetKindType" );
var idpresetKindDefault = stringIDToTypeID( "presetKindDefault" );
desc5.putEnumerated( idpresetKind, idpresetKindType, idpresetKindDefault );
var idCyn = charIDToTypeID( "Cyn " );
var desc6 = new ActionDescriptor();
var idCyn = charIDToTypeID( "Cyn " );
var idPrc = charIDToTypeID( "#Prc" );
desc6.putUnitDouble( idCyn, idPrc, 100.000000 );
var idChMx = charIDToTypeID( "ChMx" );
desc5.putObject( idCyn, idChMx, desc6 );
var idMgnt = charIDToTypeID( "Mgnt" );
var desc7 = new ActionDescriptor();
var idMgnt = charIDToTypeID( "Mgnt" );
var idPrc = charIDToTypeID( "#Prc" );
desc7.putUnitDouble( idMgnt, idPrc, 100.000000 );
var idChMx = charIDToTypeID( "ChMx" );
desc5.putObject( idMgnt, idChMx, desc7 );
var idYlw = charIDToTypeID( "Ylw " );
var desc8 = new ActionDescriptor();
var idYlw = charIDToTypeID( "Ylw " );
var idPrc = charIDToTypeID( "#Prc" );
desc8.putUnitDouble( idYlw, idPrc, 100.000000 );
var idChMx = charIDToTypeID( "ChMx" );
desc5.putObject( idYlw, idChMx, desc8 );
var idBlck = charIDToTypeID( "Blck" );
var desc9 = new ActionDescriptor();
var idBlck = charIDToTypeID( "Blck" );
var idPrc = charIDToTypeID( "#Prc" );
desc9.putUnitDouble( idBlck, idPrc, 100.000000 );
var idChMx = charIDToTypeID( "ChMx" );
desc5.putObject( idBlck, idChMx, desc9 );
var idChnM = charIDToTypeID( "ChnM" );
desc4.putObject( idType, idChnM, desc5 );
var idAdjL = charIDToTypeID( "AdjL" );
desc3.putObject( idUsng, idAdjL, desc4 );
executeAction( idMk, desc3, DialogModes.NO );
var idsetd = charIDToTypeID( "setd" );
var desc38 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref12 = new ActionReference();
var idAdjL = charIDToTypeID( "AdjL" );
var idOrdn = charIDToTypeID( "Ordn" );
var idTrgt = charIDToTypeID( "Trgt" );
ref12.putEnumerated( idAdjL, idOrdn, idTrgt );
desc38.putReference( idnull, ref12 );
var idT = charIDToTypeID( "T " );
var desc39 = new ActionDescriptor();
var idpresetKind = stringIDToTypeID( "presetKind" );
var idpresetKindType = stringIDToTypeID( "presetKindType" );
var idpresetKindCustom = stringIDToTypeID( "presetKindCustom" );
desc39.putEnumerated( idpresetKind, idpresetKindType, idpresetKindCustom );
var idCyn = charIDToTypeID( "Cyn " );
var desc40 = new ActionDescriptor();
var idCyn = charIDToTypeID( "Cyn " );
var idPrc = charIDToTypeID( "#Prc" );
desc40.putUnitDouble( idCyn, idPrc, 0.000000 );
var idChMx = charIDToTypeID( "ChMx" );
desc39.putObject( idCyn, idChMx, desc40 );
var idMgnt = charIDToTypeID( "Mgnt" );
var desc41 = new ActionDescriptor();
var idCyn = charIDToTypeID( "Cyn " );
var idPrc = charIDToTypeID( "#Prc" );
desc41.putUnitDouble( idCyn, idPrc, thePerc );
var idMgnt = charIDToTypeID( "Mgnt" );
var idPrc = charIDToTypeID( "#Prc" );
desc41.putUnitDouble( idMgnt, idPrc, thePerc );
var idYlw = charIDToTypeID( "Ylw " );
var idPrc = charIDToTypeID( "#Prc" );
desc41.putUnitDouble( idYlw, idPrc, thePerc );
var idBlck = charIDToTypeID( "Blck" );
var idPrc = charIDToTypeID( "#Prc" );
desc41.putUnitDouble( idBlck, idPrc, thePerc );
var idChMx = charIDToTypeID( "ChMx" );
desc39.putObject( idMgnt, idChMx, desc41 );
var idYlw = charIDToTypeID( "Ylw " );
var desc42 = new ActionDescriptor();
var idYlw = charIDToTypeID( "Ylw " );
var idPrc = charIDToTypeID( "#Prc" );
desc42.putUnitDouble( idYlw, idPrc, 0.000000 );
var idChMx = charIDToTypeID( "ChMx" );
desc39.putObject( idYlw, idChMx, desc42 );
var idBlck = charIDToTypeID( "Blck" );
var desc43 = new ActionDescriptor();
var idBlck = charIDToTypeID( "Blck" );
var idPrc = charIDToTypeID( "#Prc" );
desc43.putUnitDouble( idBlck, idPrc, 0.000000 );
var idChMx = charIDToTypeID( "ChMx" );
desc39.putObject( idBlck, idChMx, desc43 );
var idChnM = charIDToTypeID( "ChnM" );
desc38.putObject( idT, idChnM, desc39 );
executeAction( idsetd, desc38, DialogModes.NO );
var idsetd = charIDToTypeID( "setd" );
var desc34 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref22 = new ActionReference();
var idLyr = charIDToTypeID( "Lyr " );
var idOrdn = charIDToTypeID( "Ordn" );
var idTrgt = charIDToTypeID( "Trgt" );
ref22.putEnumerated( idLyr, idOrdn, idTrgt );
desc34.putReference( idnull, ref22 );
var idT = charIDToTypeID( "T " );
var desc35 = new ActionDescriptor();
var idBlnd = charIDToTypeID( "Blnd" );
var list10 = new ActionList();
var desc36 = new ActionDescriptor();
var idChnl = charIDToTypeID( "Chnl" );
var ref23 = new ActionReference();
var idChnl = charIDToTypeID( "Chnl" );
var idChnl = charIDToTypeID( "Chnl" );
var idMgnt = charIDToTypeID( "Mgnt" );
ref23.putEnumerated( idChnl, idChnl, idMgnt );
desc36.putReference( idChnl, ref23 );
var idSrcB = charIDToTypeID( "SrcB" );
desc36.putInteger( idSrcB, 0 );
var idSrcl = charIDToTypeID( "Srcl" );
desc36.putInteger( idSrcl, 0 );
var idSrcW = charIDToTypeID( "SrcW" );
desc36.putInteger( idSrcW, 62 );
var idSrcm = charIDToTypeID( "Srcm" );
desc36.putInteger( idSrcm, 62 );
var idDstB = charIDToTypeID( "DstB" );
desc36.putInteger( idDstB, 0 );
var idDstl = charIDToTypeID( "Dstl" );
desc36.putInteger( idDstl, 0 );
var idDstW = charIDToTypeID( "DstW" );
desc36.putInteger( idDstW, 255 );
var idDstt = charIDToTypeID( "Dstt" );
desc36.putInteger( idDstt, 255 );
var idBlnd = charIDToTypeID( "Blnd" );
list10.putObject( idBlnd, desc36 );
desc35.putList( idBlnd, list10 );
var idLefx = charIDToTypeID( "Lefx" );
var desc37 = new ActionDescriptor();
var idScl = charIDToTypeID( "Scl " );
var idPrc = charIDToTypeID( "#Prc" );
desc37.putUnitDouble( idScl, idPrc, 100.000000 );
var idLefx = charIDToTypeID( "Lefx" );
desc35.putObject( idLefx, idLefx, desc37 );
var idLyr = charIDToTypeID( "Lyr " );
desc34.putObject( idT, idLyr, desc35 );
executeAction( idsetd, desc34, DialogModes.NO );
return app.activeDocument.activeLayer
};
/* function for curves */
function theCurvesMix (total, channelNumber) {
var thePoint = 255 - (256 / channelNumber * total / 100);
var idMk = charIDToTypeID( "Mk " );
var desc7 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref4 = new ActionReference();
var idAdjL = charIDToTypeID( "AdjL" );
ref4.putClass( idAdjL );
desc7.putReference( idnull, ref4 );
var idUsng = charIDToTypeID( "Usng" );
var desc8 = new ActionDescriptor();
var idType = charIDToTypeID( "Type" );
var desc9 = new ActionDescriptor();
var idpresetKind = stringIDToTypeID( "presetKind" );
var idpresetKindType = stringIDToTypeID( "presetKindType" );
var idpresetKindDefault = stringIDToTypeID( "presetKindDefault" );
desc9.putEnumerated( idpresetKind, idpresetKindType, idpresetKindDefault );
var idCrvs = charIDToTypeID( "Crvs" );
desc8.putObject( idType, idCrvs, desc9 );
var idAdjL = charIDToTypeID( "AdjL" );
desc7.putObject( idUsng, idAdjL, desc8 );
executeAction( idMk, desc7, DialogModes.NO );
var idsetd = charIDToTypeID( "setd" );
var desc17 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref9 = new ActionReference();
var idAdjL = charIDToTypeID( "AdjL" );
var idOrdn = charIDToTypeID( "Ordn" );
var idTrgt = charIDToTypeID( "Trgt" );
ref9.putEnumerated( idAdjL, idOrdn, idTrgt );
desc17.putReference( idnull, ref9 );
var idT = charIDToTypeID( "T " );
var desc18 = new ActionDescriptor();
var idpresetKind = stringIDToTypeID( "presetKind" );
var idpresetKindType = stringIDToTypeID( "presetKindType" );
var idpresetKindCustom = stringIDToTypeID( "presetKindCustom" );
desc18.putEnumerated( idpresetKind, idpresetKindType, idpresetKindCustom );
var idAdjs = charIDToTypeID( "Adjs" );
var list4 = new ActionList();
var desc19 = new ActionDescriptor();
var idChnl = charIDToTypeID( "Chnl" );
var ref10 = new ActionReference();
var idChnl = charIDToTypeID( "Chnl" );
var idChnl = charIDToTypeID( "Chnl" );
var idCmps = charIDToTypeID( "Cmps" );
ref10.putEnumerated( idChnl, idChnl, idCmps );
desc19.putReference( idChnl, ref10 );
var idCrv = charIDToTypeID( "Crv " );
var list5 = new ActionList();
var desc20 = new ActionDescriptor();
var idHrzn = charIDToTypeID( "Hrzn" );
desc20.putDouble( idHrzn, thePoint );
var idVrtc = charIDToTypeID( "Vrtc" );
desc20.putDouble( idVrtc, 0.000000 );
var idPnt = charIDToTypeID( "Pnt " );
list5.putObject( idPnt, desc20 );
var desc21 = new ActionDescriptor();
var idHrzn = charIDToTypeID( "Hrzn" );
desc21.putDouble( idHrzn, thePoint + 4 );
var idVrtc = charIDToTypeID( "Vrtc" );
desc21.putDouble( idVrtc, 255.000000 );
var idPnt = charIDToTypeID( "Pnt " );
list5.putObject( idPnt, desc21 );
desc19.putList( idCrv, list5 );
var idCrvA = charIDToTypeID( "CrvA" );
list4.putObject( idCrvA, desc19 );
desc18.putList( idAdjs, list4 );
var idCrvs = charIDToTypeID( "Crvs" );
desc17.putObject( idT, idCrvs, desc18 );
executeAction( idsetd, desc17, DialogModes.NO );
app.activeDocument.activeLayer.grouped = true;
return app.activeDocument.activeLayer
};
Photo of joshua withers

joshua withers

  • 84 Posts
  • 16 Reply Likes
Thanks Cristoph but I am getting syntax errors. Could you email me the script or the adj layers? jjwithers at g mail dot com
Photo of joshua withers

joshua withers

  • 84 Posts
  • 16 Reply Likes
Cristoph has a good solution. But it sure would be nice to have this feature within the actual application as a soft-proof-gamut warning type of option.