Created Date: 03 Feb, 2022 11:08
Last Modifed Date: 03 Feb, 2022 11:08


Introduction

Mako 6.4.0 is a full product release that introduces some new and improved features, as well as fixes for identifiable support issues, labeled MAKOSUP-XXXXX.

New features include:

  • support for importing Adobe Photoshop documents (.PSD)
  • Windows 11 is now officially supported
  • new, faster APIs for splitting and merging PDFs, targeting high page count operations (for example splitting a 100,000-page PDF into two 50,000-page PDFs)
  • ability to encrypt a PDF using a private key certificate as the encryption key
  • improved support for DeviceLink ICC profiles when color converting
  • support for PDF/A-2u

New or improved features

MAKO-3455

Add support for Adobe Photoshop PSD image format

   

This new feature meets a customer requirement to read Photoshop images directly into Mako without the need for intermediate conversion to an existing supported image format, such as TIFF, JPEG, or PNG.

The new input class works in the same way as other image decoders; for example, this code snippet loads a Photoshop file into an IDOMImage:

IDOMImagePtr image = IDOMPSDImage::create(jawsMako,
IInputStream::createFromFile(jawsMako,"MyPhotoshopImage.psd"));
CPP

Not shown here is a third parameter, layerIndex, that selects an image from the given layer of a multi-layered Photoshop file. A layerIndex of 0 (the default when omitted) returns the composite image, so layers are numbered from one.

Gray, RGB, and CMYK colorspaces, with or without an alpha channel, are supported. Bit depths of 8-, 16-, & 32-bit are supported. Indexed color is supported.

Duotone, multi-channel images and Photoshop’s PSB (large image format) are not supported. Attempting to load these throws a “not supported” exception.

This implementation does not include an image encoder, meaning it is not possible to save an image from Mako to a Photoshop file.

A new example, imageconverter, shows the new class in action. You can find it in the MakoApps folder of the Mako distribution.

MAKO-3795

Image converter example


A new example is added to the MakoApps folder (of the Mako distribution) that shows how to convert from one image type to another. It is the first example that demonstrates use of the Photoshop image input API introduced in this release of Mako.

MAKO-3672

Improve Split and Merge performance for PDF


Mako 6.4.0 introduces two new classes, IPDFPageExtractor() and IPDFPageInserter(), that make possible high performance split and merge operations, exclusively on PDFs.

For example, the following code combines all the PDF files found in the source folder ("test100"):

// Instantiate Mako
const auto mako = IJawsMako::create();
IJawsMako::enableAllFeatures(mako);

// Create a directory iterator for the source folder
const auto folderIterator = fs::directory_iterator(std::filesystem::path("test100"));

// Create page inserter that will allows pages to be added to the first of those files
const auto pageInserter = 
  IPDFPageInserter::create(mako, 
    IInputStream::createFromFile(mako, U8String(begin(folderIterator)>path().u8string())));
        
// Timer
const clock_t start = clock();

// Starting with the second, append all pages of each file found
for (auto iterator = begin(folderIterator).operator++(); iterator != end(folderIterator); ++iterator)
  pageInserter->insertFrom(
    IInputStream::createFromFile(
      mako, U8String(iterator->path().generic_u8string())), pageInserter->getNumPages(), 0);

// Now save, and we are done
pageInserter->save(IOutputStream::createToFile(mako, "combined.pdf"));

const clock_t end = clock();
const auto elapsed_secs = (end - start) / static_cast<double>(CLOCKS_PER_SEC);
std::wcout << L"Elapsed time: " << elapsed_secs << L" seconds." << std::endl;
CPP

The important characteristic of these new APIs is their speed. Compared to Mako DOM-based splitting and merging, which essentially work by copying pages individually, these new methods operate in a radically different way. This is particularly evident for high page count operations, for example splitting a 200,000-page PDF into two 100,000-page PDFs. A DOM-based splitter may take several minutes to complete the task, whereas code based on these new APIs is able to carry out the task in seconds.

MAKO-3641

Better ToUnicode handling for simple fonts


This work began with a customer case to improve Unicode recovery, the process whereby downstream consumers of a Mako-processed PDF can extract meaningful content from text presented on the page.

The success of this effort led to support for PDF/A-2u. See MAKO-3757 below.

MAKO-3757

Add support for PDF/A-2u


The PDF Association describes PDF/A-2u thus: “New to PDF/A-2 is the conformance level PDF/A-2u (“u” for “Unicode”). It simplifies the text searching and copying of Unicode text for PDF documents.”

As Mako is already engineered to emit Unicode as much as possible, using a variety of techniques to identify the correct Unicode character encoding when the information is missing in the source, it was straightforward to combine this with the existing support for PDF/A to add the capability for writing PDFs to meet this standard.

To make use of it, simply set the PDF version of the IPDFOutput class. For example:

IPDFOutputPtr output = IPDFOutput::create(jawsMako);

output->setVersion(IPDFOutput:: ePDFA2u); 
CPP

A quick way to give this new output a try is to use the standard Mako example, makoconverter, with this parameter:

makoconverter input.pdf output.pdf PDFVersion=PDF/A-2u

MAKO-3723

Add APIs to IDistiller to match Jaws PDFLib more closely


IDistiller, Mako’s class for direct PostScript to PDF conversion, now offers additional conversion parameters, governing such things as image downsampling, overprint simulation, linearization, thumbnail generation, and more.

MakoDistillerCmd is the sample command-line application that is included in the Mako distribution, pre-built for Windows. Developed to exercise the IDistiller class, it provides control over virtually all the PostScript to PDF conversion parameters that IDistiller offers.

The online documentation for this and the makodistiller sample have been updated to reflect the improvements in this release. You can find them here:

Comparison of testlibpdfcmd to makodistillercmd

Command-line parameters – makodistiller

MAKO-3796

Support device link profiles when converting colors using IColorManager


DeviceLink profiles are supported for color conversions that take place in the Mako renderer, but the implementation did not extend to the IColorManager::convertColors() methods. With this development, that is now possible. The code snippet below shows how to use a DeviceLink by providing a null target space.

// Normal (using an input and an output profile)
cmm->convertColors(1, false, deviceCmyk, deviceGray, 
	eRelativeColorimetric, eBPCDefault, inComponents, &outRegular);

// DeviceLink
cmm->convertColors(1, false, deviceLinkSpace, IDOMColorSpacePtr(),
	eRelativeColorimetric, eBPCDefault, inComponents, &outDeviceLink);

CPP

MAKO-3728

MAKOSUP-10862 Encrypt a PDF using a digital certificate


This development meets a customer requirement to enable certificate-based public key encryption for output. Decryption at input is also implemented.

Input A new API, IPDFInput::setPkcs12() allows the user to specify a pkcs12/pfx to use for the private key. You also need to set a password to decrypt the private key contained in the pkcs12 data, which is used to decrypt the document key that is then used to decrypt the document. This was tested with 128- and 256-bit encrypted documents, produced by Acrobat.

Output A new overload for the API IPDFOutput::setEncryption() is added that takes the recipient information and the desired key length. Mako supports AES-128 and AES-256.

A code example is available.

MAKO-3780

Create new builds of Mako for RHEL7 and RHEL8


Two new Linux distributions are added to the Mako distribution:

  • Red Hat Enterprise Linux v7.9
  • Red Hat Enterprise Linux v8.4

MAKO-3637/8

Improve streaming for C# & Java developers


Improvements are made internally to hide the SWIG-generated class names, thereby making the streaming classes more palatable to C# and Java developers. However, the main effort is the development of new example code that implements helper classes that show how to use the random-access streaming callback mechanism.

Called nativestream, the new example can be found in the simpleexamples folder of the C# distribution (nativestream.cs), the Java distribution (nativestream.java) and to round out the set, in the regular C++ distribution (nativestream.cpp).

MAKO-1629

Embedded fonts are duplicated in the output of a combine operation


When combining PDFs with a Mako-based utility, the same font appeared to be embedded more than once. This is counter to the normal behavior that sees Mako merge fonts and font subsets when writing to PDF.

This change improves the generation of CID fonts (the example was Japanese) that prevents the font duplication in the PDF output.

MAKO-3731

Ensure that non-random-access streams work with IDOMFontOpenType


During testing of Mako sample code, an error was encountered when attempting to load a font from a fixed stream. Although the Mako API allowed it, internally OpenType fonts can't work unless the stream is seekable. To solve this, a convenience API is added to convert a fixed stream to random access by making a temporary copy.

The new API returns an IRAInputStreamPtr

IRAInputStreamPtr createRandomAccessFromNonRandomAccess(IEDLClassFactory *pFactory, const IInputStreamPtr &stream);
CPP

Mako calls this automatically as required, meaning that user code need not be amended.

MAKO-3698

Speed up IJPDS stitching performance


We have continued to develop the IJPDS direct-to-raster feature that was introduced in Mako 6.3.0. Its purpose is to retrieve a page raster from an IJPDS input as quickly as possible, when the primary intent is to send the raster on to an output device.

The IPage::getPageRaster() class offers rotation and stitching (concatenation of rasters into one). The purpose of the work in this release is to significantly improve the performance of those additional operations.

Testing demonstrates the following improvements:

Test Id.

Rotate

Stitch

6.3.1
m:ss.0

6.4.0
m:ss.0

% Improvement

1

None

None

0:59.9

0:46.3

23%

2

90

None

5:54.7

1:19.3

78%

3

180

None

4:01.8

0:50.7

79%

4

270

None

5:52.2

1:19.5

77%

5

None

Vertical

1:28.8

0:46.0

48%

6

90

Vertical

6:10.0

1:10.8

81%

7

180

Vertical

4:29.8

0:50.1

81%

8

270

Vertical

6:08.4

1:11.1

81%

9

None

Horizontal

1:30.2

0:46.4

49%

10

90

Horizontal

6:37.6

1:11.6

82%

11

180

Horizontal

4:31.1

0:50.4

81%

12

270

Horizontal

6:36.1

1:11.5

82%

For these tests, output is to raster without writing to disk.

MAKO-3754

Stop using exceptions for flow control when writing to our outputs


When Mako writes to outputs, most do so while avoiding calling IDocumentAssembly::getNumDocuments() or IDocument::getNumPages(), because inputs that work on streaming data (PCL, PXL, IJPDS, and streaming XPS) need to wait until that information becomes available, which in the worst case means the entire stream needs to be written and parsed.

To avoid this, the outputs only attempt to request the next page and the next document to allow for efficient consumption and lower memory use. To do this, they call IDocument::getPage() and IDocumentAssembly::getDocument(), relying on an exception with the right error code to know that they have run out of documents or pages and can either move on to the next document or complete the job.

While this mechanism works very well, it has resulted in queries to the support list from developers working with Mako who are concerned that something is awry.

The solution is to add two new public APIs:

IDocument::pageExists()
IDocumentAssembly::documentExists()
CPP

The writing loops for PS, PDF, PXL, and PCL output have been updated to use the new APIs, preventing the confusing exceptions seen previously.

MAKO-3697

Objects missing on output using -separate option for fts_38xx.xps


A rendering issue observed during routine testing has been fixed in this release.

MAKO-3745

There is no actionable information when Mako initialization fails due to the default cache directory not being suitable


When Mako is initialized in an environment where the process doesn't have permission to write to the default cache directory, an undefined error is thrown.

To make it easier for developers to track down this easily rectified error, a more meaningful message is passed when this exception is thrown, for example:

Exception thrown: [jawsmako\jawsmako.cpp:371] A general error occurred: Creating cache directory "C:\Users\jsmith\AppData\Local\JawsMako\" has failed


MAKO-3815

Java SWIG distributions are missing example source code for simplexamples and others


It was noted that the C# distribution included source for the various examples that are included with Mako, but that they were missing from the Java distribution. This is now rectified.

Support issues

This section describes improvements made to Mako in response to support requests.

MAKO-3727

MAKOSUP-10759 getPageText from IPageLayout returns text runs that are joined in wrong order


The IPageLayout class collects successive lines of characters into a single string. For the customer exhibit, the lines of text were spaced so closely vertically that only every other line was concatenated into the result. To solve this, a new parameter is added to IPageLayout. LineSpacingThreshold; it can be set to a negative value (from the default is 0.0) to allow consecutive lines of text to be correctly interpreted when their bounding boxes overlap vertically.

MAKO-3699

MAKOSUP-10785 Mako returns swapped mediabox for some PCL5 documents?


An improvement is made to the IPCL5Input class to improve interpretation of page orientation so that the page, when converted to PDF and viewed, appears in the correct orientation for reading.

MAKO-3707

MAKOSUP-10806 Problem with Unicode in ink name?


This issue is caused by ink names found in a PDF that are not encoded with Unicode (that is, the recommendation in the PDF specification for strings of this nature). Unfortunately, not all tools adhere to this convention. Some use Shift-JIS, others (as in this case) use PDFDocEncoding.

Mako now first converts to UTF-8; if this fails, it tries Shift-JIS encoding and if this also fails, PDFDocEncoding. This solves the problem for the customer exhibit and other cases the Mako team have encountered.

MAKO-3722

MAKOSUP-10815 Some space characters not extracted


The IPageLayout class interprets the horizontal advance between characters as one or more spaces, based on the character size. A parameter, VirtualSpaceThreshold, determines what distance between successive characters is considered a space. This change relaxes the lower limit of this value from 0.5 to 0.0 as a value below 0.5, which was required to obtain the correct result for the customer exhibit.

MAKO-3663

MAKOSUP-10839 Missing objects rendering a file


A mismatch between a narrow page element and a clipping path led to an object being dropped from rasterized output. Now fixed.

MAKO-3661

MAKOSUP-10841 MakoConverter removing bold fonts on PDF documents


The cause of this problem is conflicting information in the FontDescriptor entries for unembedded fonts. The StemV entries indicate a light font (that is, a thin appearance), but the font names – ArialBold, ArialBoldItalic in this case – suggest otherwise.

Mako now ignores FontDescriptor StemV/FontWeight in favor of information gleaned from the font name when:

  • The weight indicated by the FontDescriptor is in the normal range or lighter and the font name indicates bolder than normal
  • The weight indicated by the FontDescriptor is in the normal range or bolder and the font name indicates lighter than normal

An environment variable is added that restores previous behavior should it be required:

J_EMUFONT_PREFER_FONTDESCRIPTOR_METRICS_OVER_FONTNAME_WEIGHT

It can be set to anything; its presence is what matters.

MAKO-3673

MAKOSUP-10842 Font encoding error in imported pdf


The customer case uses a CIDFont, using an Identity-H CMap, a descendent with an Identity ordering, and a CIDToGIDMap of Identity (that is, the character codes in the PDF map directly to Glyph IDs on the output). As the font is unembedded, unless exactly the same font is available, the glyphs (character appearances) in the output are far from guaranteed to match the original.

Mako now uses a different approach to deal with this situation, improving the output and matching that of Acrobat.

MAKO-3724

MAKOSUP-10843 Exception in IJawsRenderer::renderSeparations()


Improvement to Mako’s custom transform implementation to deal with the customer case was required. Now fixed.

MAKO-3732

MAKOSUP-10850 Retrieve ICC profile name - can it be made clearer?


A Mako customer was understandably confused by this. For example, it’s possible to retrieve information about an ICC profile (such as the version, component names) from APIs in IDOMICCProfile, but in some other cases (such as the profile name and the profile color space) a call to IColorManager is needed.

To improve this situation, convenience APIs are added that internally call the IColorManager to do their work, but are simpler, easier to discover, and use. The new APIS are:

IDOMICCProfile::getProfileName()
IDOMICCProfile::getProfileColorSpace()
CPP

Information that IColorManager provides, such as colorant names, number of components, and rendering intent, are provided by existing IDOMColorSpace APIs inherited by IDOMICCProfile and so nothing was required for those.

MAKO-3709

MAKOSUP-10851 MAKO 6.3.0 Error when compiling with MUSL library


A library added in Mako 6.3.0 (libjpeg-turbo) was exporting symbols that clashed with a customer’s project. Now fixed.

MAKO-3711

MAKOSUP-10856 JawsMako.Separator not available


The ISeparator class was excluded from the SWIG build that generates the C# & Java bindings. Now included.

MAKO-3778

MAKOSUP-10872 Missing font triggers Internal RIP Error


The customer exhibit is a badly broken PDF. It contains a font reference (/F1), the target of which is completely missing from the file. Although this triggers errors in PDF preflighting utilities, Acrobat can display the page.

The bad font reference appears in text added as a watermark to indicate the licensing status of the host application.

Mako now renders this text, but the change carries risk, as several assumptions must be made about the missing font. Therefore, support for this must be enabled by setting an environment variable:

J_ATTEMPT_TF_WITH_MISSING_FONT_RESOURCE

It can be set to anything; its presence is what matters.

MAKO-3753

MAKOSUP-10873 Support template form of createInstance<IDOMPathGeometryBuilder>()


The definition of this API was incorrect and preventing creation of an IDOMPathGeometryBuilder object. Now corrected.

MAKO-3708

MAKOSUP-10874 Provide an API to definitively indicate if a font is subsetted


There are two requirements:

  • A way to detect that a font was subset in an incoming PDF
  • When writing such fonts out to PDF output with subsetting disabled, that the subset fonts from the original are still described as subset in the resulting PDF

Although it is possible to infer that a font is subsetted by examining the font name (as by convention, a subsetted font’s FontName entry has a tag consisting of six uppercase characters, followed by a "+" character, for example, EOODIA+Poetica), a more definitive indication is required.

A new API, IDOMFontOpenType::getSubsetted(), meets the first requirement.

For the second requirement, Mako now prepends a tag (as described above) to preserve the indication that the font is actually a subset, during PDF output.

Distribution

MAKO Version 6.4.0 is built for the following platforms:

  • iOS
  • macOS
  • Linux (for Debian-based distributions; for example, Ubuntu, Mint)
  • Linux (Centos)
  • Linux (Red Hat Enterprise v7.0)
  • Linux (Red Hat Enterprise v8.4)
  • Linux (for Debian Buster) (ARM32 for Raspberry Pi)
  • Linux (for Debian Stretch)
  • Linux (for Debian Bullseye)
  • Linux (for MUSL distributions; for example, Alpine Linux)
  • Linux (for Ubuntu 20.04 LTS)
  • Windows (static and dynamic libs, VS 2019 (V142), x86 and x64)
  • Windows (static and dynamic libs, VS 2017 (V141), x64)

The Android build has been dropped from this release pending a tooling change. Please contact Mako support if you need a Mako release for Android later than Mako 6.1.0.

Mako supports the following programming languages:

  • C++ (Mako is written in C++)
  • C# (.Net Framework and .Net Core)
  • Java (built with OpenJDK11)
  • Python (v3.8)

The alternatives to C++ are built using SWIG (www.swig.org), which provides a translation to the native libraries found in these distribution folders:

  • Linux_SWIG_(C#-Java-Python)
  • Linux_Centos7_SWIG_(C#-Java-Python)
  • Linux_Centos8_SWIG_(C#-Java-Python)
  • Linux_Ubuntu_SWIG_(C#-Java-Python)
  • macOS_SWIG_(C#-Java-Python)
  • Windows_SWIG_(C#-Java-Python)