Output Intents
Created Date: 11 Sep, 2023 10:24
Last Modifed Date: 11 Sep, 2023 10:24
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/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, double& width, double& height);
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 Probe profile
const auto profile = std::pair<U8String, U8String>("Probev2_ICCv4.icc", "https://www.color.org/probeprofile.xalter");
// 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))));
// 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));
run->setColor(darkBlue);
paragraphs.append(ILayoutParagraph::create());
paragraphs[paraIndex]->setSpacing(P2X(6));
paragraphs[paraIndex]->addRun(run);
text = std::format(" ● {} (from {})", profile.first, profile.second);
run = ILayoutTextRun::create(text.c_str(), font, fontIndex, P2X(10));
paragraphs.append(ILayoutParagraph::create());
paragraphs[++paraIndex]->setSpacing(P2X(5));
paragraphs[paraIndex]->addRun(run);
// Picture
double width = 0;
double height = M2X(169);
const auto parrot = getImage(mako, R"(..\..\TestFiles\Parrot.png)", width, height);
paragraphs.append(ILayoutParagraph::create());
paragraphs[++paraIndex]->addRun(ILayoutImageRun::create(parrot, width, height));
// Add content to the page
fixedPage->appendChild(layout->layout(paragraphs));
auto output = IPDFOutput::create(mako);
output->setPreset("PDF/X-4");
// Load profile from local storage
const auto iccProfile = IDOMICCProfile::create(mako, IInputStream::createFromFile(mako, R"(..\..\TestFiles\)" + profile.first));
// Output intent metadata
const U8String subtype = "GTS_PDFX";
const U8String registryName = "http://www.color.org";
const U8String outputCondition = profile.first;
const U8String outputConditionIdentifier = profile.first;
const U8String info = "Output Intent test";
// Create an intent and add to PDF
const auto outputIntent = IOutputIntent::create(mako, subtype, outputCondition, outputConditionIdentifier, registryName, info, iccProfile);
output->setOutputIntent(outputIntent);
// Write PDF
output->writeAssembly(assembly, "OutputIntentExample.pdf");
// 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 and scale proportionately to specified width and/or height
IDOMImagePtr getImage(const IJawsMakoPtr& mako, const U8String& imageFile,
double& width, double& height)
{
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());
}
const auto frame = image->getImageFrame(mako);
const double imageWidth = frame->getWidth();
const double imageHeight = frame->getHeight();
const double aspectRatio = imageWidth / imageHeight;
// If neither dimensions have been set then return the image with its 1:1 pixel dimensions
if (width == 0.0 && height == 0.0)
{
width = imageWidth;
height = imageHeight;
return image;
}
// If both dimensions have been set then we're done
if (width > 0.0 && height > 0.0)
return image;
// Calculate the missing dimension
if (height == 0.0)
height = width / aspectRatio;
else
width = height * aspectRatio;
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 static JawsMako.jawsmakoIF_csharp;
namespace SaveWithOutputIntentCS;
class SaveWithOutputIntent
{
static void Main(string[] args)
{
// The ICC Probe profile
var profile = Tuple.Create("Probev2_ICCv4.icc", @"https://www.color.org/probeprofile.xalter");
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))));
// 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));
run.setColor(darkBlue);
paragraphs.append(ILayoutParagraph.create());
paragraphs[paraIndex].setSpacing(P2X(6));
paragraphs[paraIndex].addRun(ILayoutRun.fromRCObject(run.toRCObject()));
text = $" ● {profile.Item1} (from {profile.Item2})";
run = ILayoutTextRun.create(text, font, fontIndex, P2X(10));
paragraphs.append(ILayoutParagraph.create());
paragraphs[++paraIndex].setSpacing(P2X(5));
paragraphs[paraIndex].addRun(ILayoutRun.fromRCObject(run.toRCObject()));
// Picture
double width = 0;
double height = M2X(169);
var parrot = GetImage(ref mako, @"..\..\..\..\TestFiles\Parrot.png", ref width, ref height);
paragraphs.append(ILayoutParagraph.create());
paragraphs[++paraIndex].addRun(ILayoutImageRun.create(parrot, width, height));
// Add content to the page
fixedPage.appendChild(layout.layout(paragraphs));
using var output = IPDFOutput.create(mako);
output.setPreset("PDF/X-4");
// Load profile from local storage
var iccProfile = IDOMICCProfile.create(mako, IInputStream.createFromFile(mako, Path.Combine(@"..\..\..\..\TestFiles\", profile.Item1)));
// Output intent metadata
var subtype = "GTS_PDFX";
var registryName = "http://www.color.org";
var outputCondition = profile.Item1;
var outputConditionIdentifier = profile.Item1;
var info = "Output Intent test";
// 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");
// 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 and scale proportionately to specified width and/or height
private static IDOMImage GetImage(ref IJawsMako mako, string imageFile, ref double width, ref double height)
{
var fileInfo = new FileInfo(imageFile);
if (!fileInfo.Exists)
{
throw new Exception($"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.");
}
var frame = image.getImageFrame(mako);
double imageWidth = frame.getWidth();
double imageHeight = frame.getHeight();
double aspectRatio = imageWidth / imageHeight;
// If neither dimensions have been set then return the image with its 1:1 pixel dimensions
if (width == 0.0 && height == 0.0)
{
width = imageWidth;
height = imageHeight;
return image;
}
// If both dimensions have been set then we're done
if (width > 0.0 && height > 0.0)
return image;
// Calculate the missing dimension
if (height == 0.0)
height = width / aspectRatio;
else
width = height * aspectRatio;
return image;
}
}