Output Intents
An output intent describes the final destination device you will use to reproduce the color in the PDF, such as the separations printing device. Output intents override working spaces during viewing and printing, but they do not convert the colors in the PDF. You can include output intents when you create PDF/X (or PDF/A) files.
Mako uses a property of the IPDFOutput
class to specify the output intent when writing a PDF. The IOutputIntent
class represents a PDF output intent and provides the create()
method that requires several items of information, described below. More detailed information regarding the information needed is described in the PDF specification 1.7 (refer to section 14.11.5).
IOutputIntent create()
The create method requires the following information, described here with some examples:
Name | Output intent property | Typical value / purpose | ||
---|---|---|---|---|
sRGB | SWOP | FOGRA39 | ||
registryName | Profile registry | http://www.color.org | http://www.color.org | http://www.color.org |
outputCondition | Output condition | sRGB IEC61966-2.1 | CGATS TR 001 (SWOP) | FOGRA39 |
outputConditionIdentifier | sRGB IEC61966-2.1 | CGATS TR 001 | FOGRA39 | |
subtype | Subtype | One of: | ||
info | Info | A human-readable text string containing additional information or comments about the intended target device or production condition. Can be left blank. | ||
profile | ICC Profile | An IDOMICCProfile . See code example for creation of this class from a file-based ICC profile. |
Worked Example
The code in the following section created a PDF, shown here displayed in Acrobat (or Acrobat Reader).
The ICC profile used for the output intent is deliberately designed to alter colors in a dramatic fashion, so that it is clear it is being used.
To see the effect on the PDF, you can open it in Adobe Acrobat Reader DC and set Edit > Preferences > Page Display > Use Overprint Preview to 'Always':
With the 'Use Overprint Preview' option set to 'Never'. | With the 'Use Overprint Preview' option set to 'Always'. |
---|---|
Code
/* -----------------------------------------------------------------------
* <copyright file="SaveWithOutputIntent.cpp" company="Global Graphics Software Ltd">
* Copyright (c) 2022-2023 Global Graphics Software Ltd. All rights reserved.
* </copyright>
* <summary>
* This example is provided on an "as is" basis and without warranty of any kind.
* Global Graphics Software Ltd. does not warrant or make any representations
* regarding the use or results of use of this example.
* </summary>
* -----------------------------------------------------------------------
*/
#include <iostream>
#include <format>
#include <filesystem>
using namespace std;
namespace fs = std::filesystem;
#include <jawsmako/jawsmako.h>
#include <jawsmako/layout.h>
#include <jawsmako/outputintent.h>
#include <jawsmako/pdfinput.h>
#include <jawsmako/pdfoutput.h>
#include <edl/idomresources.h>
using namespace JawsMako;
using namespace EDL;
#define M2X(value) ((value) / 25.4 * 96.0)
#define P2X(value) ((value) / 72.0 * 96.0)
IDOMImagePtr getImage(const IJawsMakoPtr& mako, const U8String& imageFile);
int main()
{
try
{
const auto mako = IJawsMako::create();
mako->enableAllFeatures(mako);
// Create assembly, document, page, fixed page
const auto assembly = IDocumentAssembly::create(mako);
const auto document = IDocument::create(mako);
assembly->appendDocument(document);
const auto page = IPage::create(mako);
document->appendPage(page);
const auto fixedPage = IDOMFixedPage::create(mako, M2X(250), M2X(210));
page->setContent(fixedPage);
// The ICC profiles
CEDLVector<std::pair<U8String, U8String>> profiles;
profiles.append(std::pair<U8String, U8String>("Probev2_ICCv4.icc", "https://www.color.org/probeprofile.xalter"));
profiles.append(std::pair<U8String, U8String>("GRACoL2006_Coated1v2.icc", "https://idealliance.org"));
profiles.append(std::pair<U8String, U8String>("JapanColor2011Coated.icc", "https://www.xrite.com"));
// Get a font
uint32 fontIndex;
const auto font = edlobj2IDOMFontOpenType(mako->findFont("Arial", fontIndex));
// Create a colour
const auto darkBlue = IDOMColor::createSolidRgb(mako, 0.0f, 0.0f, 0.5f);
// Create a layout
const auto layout = ILayout::create(mako);
// Add frame to hold content
layout->addFrame(ILayoutFrame::create(FRect(M2X(12), M2X(12), M2X(273), M2X(186))));
// Create template paragraphs
const auto header = ILayoutParagraph::create(ILayoutParagraph::eHALeft, P2X(6));
const auto body = ILayoutParagraph::create(ILayoutParagraph::eHALeft, P2X(6));
// A vector to point to each paragraph to be added to the frame(s)
auto paragraphs = CEDLVector<ILayoutParagraphPtr>();
uint32 paraIndex = 0;
// Create paragraphs and text runs
std::string text = "This PDF has an Output Intent referencing this ICC profile:";
auto run = ILayoutTextRun::create(text.c_str(), font, fontIndex, P2X(12), darkBlue);
paragraphs.append(header->clone());
paragraphs[paraIndex]->addRun(run);
text = std::format(" ● {} (from {})", profiles[0].first, profiles[0].second);
run = ILayoutTextRun::create(text.c_str(), font, fontIndex, P2X(10));
paragraphs.append(body->clone());
paragraphs[++paraIndex]->addRun(run);
// Picture
const auto parrot = getImage(mako, R"(..\..\TestFiles\Parrot.png)");
paragraphs.append(ILayoutParagraph::create());
paragraphs[++paraIndex]->addRun(ILayoutImageRun::create(mako, parrot, 0, M2X(169)));
// Add content to the page
fixedPage->appendChild(layout->layout(paragraphs));
// Load profile from local storage
const auto iccProfile = IDOMICCProfile::create(mako, IInputStream::createFromFile(mako, R"(..\..\TestFiles\)" + profiles[0].first));
// Output intent metadata
U8String subtype = "GTS_PDFX";
U8String registryName = "http://www.color.org";
U8String outputCondition = profiles[0].first;
U8String outputConditionIdentifier = profiles[0].first;
U8String info = "Output Intent test";
// Create a PDF output set to write PDF/X
auto output = IPDFOutput::create(mako);
output->setPreset("PDF/X-4");
// Create an intent and add to PDF (the primary, and only intent recognized for PDF/X purposes)
auto outputIntent = IOutputIntent::create(mako, subtype, outputCondition, outputConditionIdentifier, registryName, info, iccProfile);
output->setOutputIntent(outputIntent);
// Write PDF/X-compliant PDF
output->writeAssembly(assembly, "OutputIntentExample.pdf");
// Check that the PDF has an output intent
auto testDocument = IPDFInput::create(mako)->open("OutputIntentExample.pdf")->getDocument();
_ASSERT(testDocument->getOutputIntents().size() == 1);
// Demonstration of adding more than one output intent. PDF/X allows it, may suit some custom workflows
COutputIntentVect outputIntents;
outputIntents.append(outputIntent);
// We add two alternates
subtype = "GGS_DUMMY";
registryName = "https://idealliance.org";
outputCondition = profiles[1].first;
outputConditionIdentifier = profiles[1].first;
info = "The GRACoL 2006 Coated v2 from idealliance.org";
// Create an intent and add to list
outputIntent = IOutputIntent::create(mako, subtype, outputCondition, outputConditionIdentifier, registryName, info, iccProfile);
outputIntents.append(outputIntent);
subtype = "GGS_DUMMY";
registryName = "https://www.xrite.com";
outputCondition = profiles[2].first;
outputConditionIdentifier = profiles[2].first;
info = "The Japan Color 2011 profile from xrite.com";
// Create an intent and add to list
outputIntent = IOutputIntent::create(mako, subtype, outputCondition, outputConditionIdentifier, registryName, info, iccProfile);
outputIntents.append(outputIntent);
// Set multiple intents
output->setOutputIntents(outputIntents);
// Write PDF
output->writeAssembly(assembly, "OutputIntentExampleThreeIntents.pdf");
// Check that the PDF has three output intents
testDocument = IPDFInput::create(mako)->open("OutputIntentExampleThreeIntents.pdf")->getDocument();
_ASSERT(testDocument->getOutputIntents().size() == 3);
// Done
}
catch (IError& e)
{
const String errorFormatString = getEDLErrorString(e.getErrorCode());
std::wcerr << L"Exception thrown: " << e.getErrorDescription(errorFormatString) << std::endl;
return static_cast<int>(e.getErrorCode());
}
catch (std::exception& e)
{
std::wcerr << L"std::exception thrown: " << e.what() << std::endl;
return 1;
}
return 0;
}
// Get image
IDOMImagePtr getImage(const IJawsMakoPtr& mako, const U8String& imageFile)
{
IDOMImagePtr image = IDOMImagePtr();
const auto imagePath = fs::path(imageFile);
if (!exists(imagePath))
{
std::string msg = "Image file ";
msg += imageFile;
msg += " not found.";
throw std::exception(msg.c_str());
}
if (imagePath.extension() == ".jpg")
image = IDOMJPEGImage::create(mako, IInputStream::createFromFile(mako, imageFile));
if (imagePath.extension() == ".png")
image = IDOMPNGImage::create(mako, IInputStream::createFromFile(mako, imageFile));
if (imagePath.extension() == ".tif")
image = IDOMTIFFImage::create(mako, IInputStream::createFromFile(mako, imageFile));
if (!image)
{
std::string msg = "Image file ";
msg += imageFile;
msg += " could not be loaded.";
throw std::exception(msg.c_str());
}
return image;
}
/* -----------------------------------------------------------------------
* <copyright file="SaveWithOutputIntent.cs" company="Global Graphics Software Ltd">
* Copyright (c) 2022-2023 Global Graphics Software Ltd. All rights reserved.
* </copyright>
* <summary>
* This example is provided on an "as is" basis and without warranty of any kind.
* Global Graphics Software Ltd. does not warrant or make any representations
* regarding the use or results of use of this example.
* </summary>
* -----------------------------------------------------------------------
*/
using JawsMako;
using System;
using static JawsMako.jawsmakoIF_csharp;
namespace SaveWithOutputIntentCS;
class SaveWithOutputIntent
{
static void Main(string[] args)
{
// The ICC profiles
var profiles = new List<Tuple<string, string>>();
profiles.Add(Tuple.Create("Probev2_ICCv4.icc", @"https://www.color.org/probeprofile.xalter"));
profiles.Add(Tuple.Create("GRACoL2006_Coated1v2.icc", "https://idealliance.org"));
profiles.Add(Tuple.Create("JapanColor2011Coated.icc", "https://www.xrite.com"));
try
{
var mako = IJawsMako.create();
IJawsMako.enableAllFeatures(mako);
// Get a page ready to accept some DOM
using var assembly = IDocumentAssembly.create(mako);
using var document = IDocument.create(mako);
assembly.appendDocument(document);
using var page = IPage.create(mako);
document.appendPage(page);
var fixedPage = IDOMFixedPage.create(mako, M2X(250), M2X(210));
page.setContent(fixedPage);
// Get a font
var font = IDOMFontOpenType.fromRCObject(mako.findFont("Arial", out var fontIndex).toRCObject());
// Create a colour
var darkBlue = IDOMColor.createSolidRgb(mako, 0.0f, 0.0f, 0.5f);
// Create a layout
var layout = ILayout.create(mako);
// Add frame to hold content
layout.addFrame(ILayoutFrame.create(new FRect(M2X(12), M2X(12), M2X(273), M2X(186))));
// Create template paragraphs
var header = ILayoutParagraph.create(ILayoutParagraph.eHorizontalAlignment.eHALeft, P2X(6));
var body = ILayoutParagraph.create(ILayoutParagraph.eHorizontalAlignment.eHALeft, P2X(5));
// A vector to point to each paragraph to be added to the frame(s)
CEDLVectILayoutParagraph paragraphs = new CEDLVectILayoutParagraph();
uint paraIndex = 0;
// Create paragraphs and text runs
var text = "This PDF has an Output Intent referencing this ICC profile:";
var run = ILayoutTextRun.create(text, font, fontIndex, P2X(12), darkBlue);
paragraphs.append(header.clone());
paragraphs[paraIndex].addRun(ILayoutRun.fromRCObject(run.toRCObject()));
text = $" ● {profiles[0].Item1} (from {profiles[0].Item2})";
run = ILayoutTextRun.create(text, font, fontIndex, P2X(10));
paragraphs.append(body.clone());
paragraphs[++paraIndex].addRun(ILayoutRun.fromRCObject(run.toRCObject()));
// Picture
var parrot = GetImage(ref mako, @"..\..\..\..\TestFiles\Parrot.png");
paragraphs.append(ILayoutParagraph.create());
paragraphs[++paraIndex].addRun(ILayoutImageRun.create(mako, parrot, 0, M2X(169)));
// Add content to the page
fixedPage.appendChild(layout.layout(paragraphs));
// Load profile from local storage
var iccProfile = IDOMICCProfile.create(mako, IInputStream.createFromFile(mako, Path.Combine(@"..\..\..\..\TestFiles\", profiles[0].Item1)));
// Output intent metadata
var subtype = "GTS_PDFX";
var registryName = "http://www.color.org";
var outputCondition = profiles[0].Item1;
var outputConditionIdentifier = profiles[0].Item1;
var info = "Output Intent test";
// Create a PDF output set to write PDF/X
using var output = IPDFOutput.create(mako);
output.setPreset("PDF/X-4");
// Create an intent and add to PDF
var outputIntent = IOutputIntent.create(mako, subtype, outputCondition, outputConditionIdentifier, registryName, info, iccProfile);
output.setOutputIntent(outputIntent);
// Write PDF
output.writeAssembly(assembly, "OutputIntentExample.pdf");
// Check that the PDF has an output intent
var testDocument = IPDFInput.create(mako).open("OutputIntentExample.pdf").getDocument();
if(testDocument.getOutputIntents().size() != 1)
throw new ApplicationException("Output intent count not equal to 1");
// Demonstration of adding more than one output intent. PDF/X allows it, may suit some custom workflows
CEDLVectIOutputIntent outputIntents = new CEDLVectIOutputIntent();
outputIntents.append(outputIntent);
// We add two alternates
subtype = "GGS_DUMMY";
registryName = "https://idealliance.org";
outputCondition = profiles[1].Item1;
outputConditionIdentifier = profiles[1].Item1;
info = "The GRACoL 2006 Coated v2 from idealliance.org";
// Create an intent and add to list
outputIntent = IOutputIntent.create(mako, subtype, outputCondition, outputConditionIdentifier, registryName, info, iccProfile);
outputIntents.append(outputIntent);
subtype = "GGS_DUMMY";
registryName = "https://www.xrite.com";
outputCondition = profiles[2].Item1;
outputConditionIdentifier = profiles[2].Item1;
info = "The Japan Color 2011 profile from xrite.com";
// Create an intent and add to list
outputIntent = IOutputIntent.create(mako, subtype, outputCondition, outputConditionIdentifier, registryName, info, iccProfile);
outputIntents.append(outputIntent);
// Set multiple intents
output.setOutputIntents(outputIntents);
// Write PDF
output.writeAssembly(assembly, "OutputIntentExampleThreeIntents.pdf");
// Check that the PDF has three output intents
testDocument = IPDFInput.create(mako).open("OutputIntentExampleThreeIntents.pdf").getDocument();
if (testDocument.getOutputIntents().size() != 3)
throw new ApplicationException("Output intent count not equal to 3");
// Done
}
catch (MakoException e)
{
var errorFormatString = getEDLErrorString(e.m_errorCode);
Console.WriteLine("Exception thrown: " + e.m_msg);
}
catch (Exception e)
{
Console.WriteLine($"Exception thrown: {e}");
}
}
// Points (1/72") to XPS units (1/96")
public static double P2X(double value)
{
return value / 72.0 * 96.0;
}
// Millimetres to XPS units (1/96")
public static double M2X(double value)
{
return value / 25.4 * 96.0;
}
// Get image
private static IDOMImage GetImage(ref IJawsMako mako, string imageFile)
{
var fileInfo = new FileInfo(imageFile);
if (!fileInfo.Exists)
{
throw new FileNotFoundException($"Image file {imageFile} not found.");
}
IDOMImage? image = null;
if (fileInfo.Extension == ".jpg")
image = IDOMJPEGImage.create(mako, IInputStream.createFromFile(mako, imageFile));
if (fileInfo.Extension == ".png")
image = IDOMPNGImage.create(mako, IInputStream.createFromFile(mako, imageFile));
if (fileInfo.Extension == ".tif")
image = IDOMTIFFImage.create(mako, IInputStream.createFromFile(mako, imageFile));
if (image == null)
{
throw new Exception($"Image file {imageFile} could not be loaded.");
}
return image;
}
}