Creating pages with the ILayout class
Introduction
It has always been possible to add text and images to a page with Mako, but it required the use of some fairly low-level APIs to do so. Laying out an entire page could be an onerous task.
Enter the ILayout
class. Backed by a layout engine, it is specifically designed to simplify creating new page content, making it far easier to add left, right or fully justified paragraphs of text. Control is provided over fonts, their color, spacing and more. Images can be added too.
Layout process
The process begins by creating an instance of the ILayout
class:
const IJawsMakoPtr mako = IJawsMako::create();
IJawsMako::enableAllFeatures(mako);
...
// Create a layout
ILayoutPtr layout = ILayout::create(mako);
var mako = IJawsMako.create();
IJawsMako.enableAllFeatures(mako);
...
// Create a layout
var layout = ILayout.create(mako);
var mako = IJawsMako.create();
IJawsMako.enableAllFeatures(mako);
...
// Create a layout
var layout = ILayout.create(mako);
The process continues by adding frames, defined by FRect() structs that define the top-left corner (x, y) and their width and height (dX, dY). Units are XPS units, (1/96th inch), the default for Mako
gfgfgfg
The final step is to generate Mako DOM (Document Object Model) objects from the layout, which can be added to an IDOMFixedPage()
.
// Add to the page
fixedPage->appendChild(layout->layout(paragraphs));
// Add to the page
fixedPage.appendChild(layout.layout(paragraphs));
// Add to the page
fixedPage.appendChild(layout.layout(paragraphs));
More stuff to go here
Complete examples
/* --------------------------------------------------------------------------------
* <copyright file="Main.cpp" company="Global Graphics Software Ltd">
* Copyright (c) 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 <filesystem>
#include <cassert>
#include <jawsmako/jawsmako.h>
#include <jawsmako/layout.h>
#include <jawsmako/pdfoutput.h>
#include <edl/idommetadata.h>
using namespace std;
namespace fs = std::filesystem;
using namespace JawsMako;
using namespace EDL;
typedef std::pair<IDOMFontOpenTypePtr, uint32> otf;
#define MM2XPS(value) ((value) / 25.4 * 96.0)
#define PT2XPS(value) ((value) / 72.0 * 96.0)
// Get font
otf getOpenTypeFont(const IJawsMakoPtr& mako, const std::vector<U8String>& fontsToTry)
{
// Pick a font
IDOMFontPtr font;
uint32 fontIndex = 0; // In case the font is inside a TrueType collection
for (const auto& fontToTry : fontsToTry)
{
try
{
font = mako->findFont(fontToTry, fontIndex);
break;
}
catch (IEDLError&)
{
// Bad or missing font - default to Arial
font = mako->findFont("Arial", fontIndex);
}
}
if (font->getFontType() == IDOMFont::eFontTypeOpenType)
return otf{ edlobj2IDOMFontOpenType(font), fontIndex };
return otf{ nullptr, 0 };
}
// Draw a path using a solid color brush with a thickness of 1
IDOMPathNodePtr drawFrame(const IJawsMakoPtr& mako, const FRect& frameBounds)
{
const double margin = 4;
const auto border = FRect(frameBounds.x - margin, frameBounds.y - margin, frameBounds.dX + margin * 2, frameBounds.dY + margin * 2);
const auto solidBrush = IDOMSolidColorBrush::createSolidRgb(mako, 0.8f, 0.8f, 0.8f);
const auto path = IDOMPathNode::createStroked(mako, IDOMPathGeometry::create(mako, border), solidBrush);
path->setStrokeThickness(1);
return path;
}
// Add a layout, draw an outline if requested
void addFrame(const IJawsMakoPtr& mako, const ILayoutPtr& layout, const IDOMFixedPagePtr& fixedPage, FRect positionAndSize, bool drawOutline = false)
{
layout->addFrame(ILayoutFrame::create(positionAndSize));
// Draw a box where the frame is going to be placed
if (drawOutline)
fixedPage->appendChild(drawFrame(mako, positionAndSize));
}
// Create paragraph
ILayoutParagraphPtr createParagraph(double spaceAfter = 0.0,
ILayoutParagraph::eHorizontalAlignment justification = ILayoutParagraph::eHALeft,
double leading = 1.0,
double spaceBefore = 0.0)
{
const auto templateParagraph = ILayoutParagraph::create(justification);
if (spaceAfter > 0.0)
templateParagraph->setSpacing(spaceAfter);
if (spaceBefore > 0.0)
templateParagraph->setSpacing(spaceBefore, true);
templateParagraph->setLeading(leading);
return templateParagraph;
}
// 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;
}
int main(int argc, char** argv)
{
U8String lorem = "\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\nUt enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
try
{
const auto mako = IJawsMako::create();
IJawsMako::enableAllFeatures(mako);
// Get a page ready to accept some DOM
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);
page->setContent(fixedPage);
// Create a layout
const auto layout = ILayout::create(mako);
// Add some frames to hold content
bool drawBorder = false;
const uint32 margin = MM2XPS(12);
const auto widthWithMargins = fixedPage->getWidth() - margin * 2;
addFrame(mako, layout, fixedPage, FRect(margin, margin, widthWithMargins, MM2XPS(40)), drawBorder); // Banner
addFrame(mako, layout, fixedPage, FRect(margin, margin + MM2XPS(40), widthWithMargins / 3 - MM2XPS(2), MM2XPS(83)), drawBorder); // Sidebar with 2mm right margin
addFrame(mako, layout, fixedPage, FRect(margin + widthWithMargins / 3, margin + MM2XPS(40), widthWithMargins / 3 * 2, MM2XPS(83)), drawBorder); // Pic
addFrame(mako, layout, fixedPage, FRect(margin, margin + MM2XPS(125), widthWithMargins, MM2XPS(155)), drawBorder); // Body
// Get a font
const auto titleFont = getOpenTypeFont(mako, { "Arial Black" });
assert(titleFont.first);
const auto bodyFont = getOpenTypeFont(mako, { "DejaVu Sans Book", "Gill Sans", "Arial" });
assert(bodyFont.first);
const auto bodyBold = getOpenTypeFont(mako, { "DejaVu Sans Book Bold", "Gill Sans Bold", "Arial Bold" });
assert(bodyBold.first);
// Create a colour
const auto darkBlue = IDOMColor::createSolidRgb(mako, 0.0f, 0.0f, 0.5f);
CEDLSysStringVect paragraphCopy;
paragraphCopy.append("Travel Blog");
paragraphCopy.append("\xAThe beauty of where I found myself inspired me to write this.");
paragraphCopy.append("As the sun rose over the horizon, casting a warm golden hue across the landscape, a breathtaking scene unfolded before the onlooker's eyes. Standing at the water's edge, one's gaze extended out over a pristine lake that shimmered like a sheet of glass, reflecting the majestic beauty that surrounded it. The lake seemed to hold its breath, mirroring with utmost precision the awe-inspiring sight that lay just beyond its tranquil surface.");
paragraphCopy.append("Stretching magnificently into the distance, a range of rocky mountains dominated the backdrop. Each peak soared towards the heavens, their rugged surfaces etched by the passage of time and the forces of nature. The mountains stood resolute, a testament to the immense power and grandeur of the natural world. Their colors shifted subtly, painted with a breathtaking array of earthy tones \xE2\x80\x94 from deep siennas and ochres to soft grays and greens \xE2\x80\x94 all framed by the azure expanse of the sky.");
paragraphCopy.append("The sky itself seemed to be a canvas of its own, an ever-changing masterpiece of color and light. Towering cumulus clouds danced gracefully, casting dramatic shadows that gently caressed the mountains' slopes. The fluffy white clouds looked as though they were soft pillows, inviting the weary soul to rest upon their tender embrace. The sky stretched boundlessly, seeming to touch the very edges of the earth, a reminder of the vastness of the universe and the limitless possibilities that lay beyond the human imagination.");
paragraphCopy.append("As a gentle breeze whispered through the air, ripples formed across the surface of the lake, momentarily distorting the mirror-like reflection. The tiny waves moved in rhythmic harmony, lending an animated quality to the otherwise still waters. With each passing gust, the mountains appeared to ripple across the lake's surface, as though a magnificent magic spell had been cast upon the land.");
paragraphCopy.append("In the distance, a lone boat glided silently across the lake, its presence adding a touch of serenity to the already tranquil scene. The boat's wake created a delicate trail on the water, a fleeting mark of human existence in the midst of the grandeur of nature. It served as a reminder of the delicate balance between mankind and the Earth, and how nature's beauty can be both admired and preserved.");
paragraphCopy.append("Birds soared overhead, their graceful silhouettes weaving intricate patterns against the sky. They navigated the currents with effortless grace, adding life to the enchanting tableau. The soft cries of the birds mixed harmoniously with the gentle lapping of the lake's water against the shore, creating a soothing symphony that echoed through the air.");
paragraphCopy.append("");
// A vector to point to each paragraph to be added to the frame(s)
CLayoutParagraphVect paragraphs;
uint32 paraIndex = 0;
// Title
paragraphs.append(createParagraph(0, ILayoutParagraph::eHACenter, 1, 0));
auto run = ILayoutTextRun::create(paragraphCopy[0], titleFont.first, titleFont.second, PT2XPS(60));
run->setColor(darkBlue);
paragraphs[paraIndex]->addRun(run);
// Intro
paragraphs.append(createParagraph(9, ILayoutParagraph::eHAJustified, 1.3));
paragraphs[++paraIndex]->addRun(
ILayoutTextRun::create(paragraphCopy[1], bodyBold.first, bodyBold.second, 14.0));
// Side bar
paragraphs.append(createParagraph(7, ILayoutParagraph::eHAJustified, 1.2));
paragraphs[++paraIndex]->addRun(
ILayoutTextRun::create(paragraphCopy[2], bodyFont.first, bodyFont.second, 14.0));
// Break to next frame
paragraphs.append(ILayoutParagraph::create());
paragraphs[++paraIndex]->addRun(ILayoutTextRun::create("\xA", bodyFont.first, bodyFont.second, 4));
// Picture
double width = widthWithMargins / 3 * 2, height = 0;
const auto mountain = getImage(mako, R"(..\..\Images\Mountain.jpg)", width, height);
paragraphs[paraIndex]->addRun(ILayoutImageRun::create(mountain, width, height));
for (uint32 paraCopyIndex = 3; paraCopyIndex < 8; ++paraCopyIndex)
{
paragraphs.append(createParagraph(7, ILayoutParagraph::eHAJustified, 1.2));
paragraphs[++paraIndex]->addRun(
ILayoutTextRun::create(paragraphCopy[paraCopyIndex], bodyFont.first, bodyFont.second, 14.0));
}
const auto david = getOpenTypeFont(mako, { "David Regular" });
assert(david.first);
const auto arial = getOpenTypeFont(mako, { "Arial" });
assert(arial.first);
// Right-to-left language test
String david_header = L"סביבות מגורים - מהביוספירה ועד לנישה האקולוגית";
String david_para = L".מרכזו של כדור-הארץ נמצא כמעט 6400 קילומטר מתחת לפני הקרקע. עד כה הצליח האדם להגיע רק לעומק של שמונה קילומטרים בקירוב. אולם כבר היום ידוע לנו כי קילומטרים ספורים מתחת לכפות רגלינו כבר עולה טמפרטורת הארץ למידה שאיננה מאפשרת קיום של חיים. קילומטרים ספורים מעל לראשנו האוויר נעשה דליל וקר, ולא מתאפשר בו קיום של חיים. בתווך מצויה הביוספירה (ביו=חיים, ספירה=עולם) ¬– שכבה דקה על פני כדור-הארץ שעובייה כקליפת תפוח-העץ ביחס לתפוח השלם, אשר בה מתקיים כל עושר החיים המוכר לנו. הביוספירה כוללת את מכלול החיים בים וביבשה, במחילות ובין הרגבים שמתחת לפני הקרקע ובשכבות האוויר הסמוכות לקרקע. הביוספירה היא סביבת החיים הגדולה ביותר המוכרת לנו";
paragraphs.append(createParagraph(8, ILayoutParagraph::eHARight));
paragraphs[++paraIndex]->addRun(ILayoutTextRun::create(StringToU8String(david_header), david.first, david.second, 16.0));
paragraphs.append(createParagraph(6.5, ILayoutParagraph::eHARight));
paragraphs[++paraIndex]->addRun(ILayoutTextRun::create(StringToU8String(david_para), arial.first, arial.second, 13.0));
// Add to the page
fixedPage->appendChild(layout->layout(paragraphs));
// Write PDF
const auto output = IPDFOutput::create(mako);
output->setParameter("Producer", "Mako Layout Engine");
output->writeAssembly(assembly, "MyFirstLayout(CPP).pdf");
// Done
}
catch (IError& e)
{
const auto errorFormatString = getEDLErrorString(e.getErrorCode());
std::wcerr << L"Exception: " << 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;
}
/* --------------------------------------------------------------------------------
* <copyright file="Program.cs" company="Global Graphics Software Ltd">
* Copyright (c) 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 LayoutExampleCS;
class Program
{
static void Main(string[] args)
{
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);
page.setContent(fixedPage);
// Create a layout
var layout = ILayout.create(mako);
// Add some frames to hold content
bool drawBorder = false;
var margin = MM2XPS(12);
var widthWithMargins = fixedPage.getWidth() - margin * 2;
AddFrame(ref mako, ref layout, ref fixedPage, new FRect(margin, margin, widthWithMargins, MM2XPS(40)), drawBorder); // Banner
AddFrame(ref mako, ref layout, ref fixedPage, new FRect(margin, margin + MM2XPS(40), widthWithMargins / 3 - MM2XPS(2), MM2XPS(83)), drawBorder); // Sidebar with 2mm right margin
AddFrame(ref mako, ref layout, ref fixedPage, new FRect(margin + widthWithMargins / 3, margin + MM2XPS(40), widthWithMargins / 3 * 2, MM2XPS(83)), drawBorder); // Pic
AddFrame(ref mako, ref layout, ref fixedPage, new FRect(margin, margin + MM2XPS(125), widthWithMargins, MM2XPS(155)), drawBorder); // Body
// Get a font
var titleFont = GetOpenTypeFont(mako, new List<string>(new[] { "Arial Black" }));
var bodyFont = GetOpenTypeFont(mako, new List<string>(new[] { "DejaVu Sans Book", "Gill Sans", "Arial" }));
var bodyBold = GetOpenTypeFont(mako, new List<string>(new[] { "DejaVu Sans Book Bold", "Gill Sans Bold", "Arial Bold" }));
// Create a colour
var darkBlue = IDOMColor.createSolidRgb(mako, 0.0f, 0.0f, 0.5f);
CEDLVectString paragraphCopy = new CEDLVectString();
paragraphCopy.append("Travel Blog");
paragraphCopy.append("\nThe beauty of where I found myself inspired me to write this.");
paragraphCopy.append("As the sun rose over the horizon, casting a warm golden hue across the landscape, a breathtaking scene unfolded before the onlooker's eyes. Standing at the water's edge, one's gaze extended out over a pristine lake that shimmered like a sheet of glass, reflecting the majestic beauty that surrounded it. The lake seemed to hold its breath, mirroring with utmost precision the awe-inspiring sight that lay just beyond its tranquil surface.");
paragraphCopy.append("Stretching magnificently into the distance, a range of rocky mountains dominated the backdrop. Each peak soared towards the heavens, their rugged surfaces etched by the passage of time and the forces of nature. The mountains stood resolute, a testament to the immense power and grandeur of the natural world. Their colors shifted subtly, painted with a breathtaking array of earthy tones \u2014 from deep siennas and ochres to soft grays and greens \u2014 all framed by the azure expanse of the sky.");
paragraphCopy.append("The sky itself seemed to be a canvas of its own, an ever-changing masterpiece of color and light. Towering cumulus clouds danced gracefully, casting dramatic shadows that gently caressed the mountains' slopes. The fluffy white clouds looked as though they were soft pillows, inviting the weary soul to rest upon their tender embrace. The sky stretched boundlessly, seeming to touch the very edges of the earth, a reminder of the vastness of the universe and the limitless possibilities that lay beyond the human imagination.");
paragraphCopy.append("As a gentle breeze whispered through the air, ripples formed across the surface of the lake, momentarily distorting the mirror-like reflection. The tiny waves moved in rhythmic harmony, lending an animated quality to the otherwise still waters. With each passing gust, the mountains appeared to ripple across the lake's surface, as though a magnificent magic spell had been cast upon the land.");
paragraphCopy.append("In the distance, a lone boat glided silently across the lake, its presence adding a touch of serenity to the already tranquil scene. The boat's wake created a delicate trail on the water, a fleeting mark of human existence in the midst of the grandeur of nature. It served as a reminder of the delicate balance between mankind and the Earth, and how nature's beauty can be both admired and preserved.");
paragraphCopy.append("Birds soared overhead, their graceful silhouettes weaving intricate patterns against the sky. They navigated the currents with effortless grace, adding life to the enchanting tableau. The soft cries of the birds mixed harmoniously with the gentle lapping of the lake's water against the shore, creating a soothing symphony that echoed through the air.");
paragraphCopy.append("");
// A vector to point to each paragraph to be added to the frame(s)
CEDLVectILayoutParagraph paragraphs = new CEDLVectILayoutParagraph();
uint paraIndex = 0;
// Create paragraphs and add text runs to them
// Title
paragraphs.append(ILayoutParagraph.create(ILayoutParagraph.eHorizontalAlignment.eHACenter));
var run = ILayoutTextRun.create(paragraphCopy[0], titleFont.Item1, titleFont.Item2, PT2XPS(60));
run.setColor(darkBlue);
paragraphs[paraIndex].addRun(ILayoutRun.fromRCObject(run.toRCObject()));
// Intro
paragraphs.append(CreateParagraph(9, ILayoutParagraph.eHorizontalAlignment.eHAJustified, 1.3));
paragraphs[++paraIndex].addRun(
ILayoutTextRun.create(paragraphCopy[1], bodyBold.Item1, bodyBold.Item2, 14.0));
// Side bar
paragraphs.append(CreateParagraph(7, ILayoutParagraph.eHorizontalAlignment.eHAJustified, 1.2));
paragraphs[++paraIndex].addRun(
ILayoutTextRun.create(paragraphCopy[2], bodyFont.Item1, bodyFont.Item2, 14.0));
// Break to next frame
paragraphs.append(ILayoutParagraph.create());
paragraphs[++paraIndex].addRun(ILayoutTextRun.create("\n", bodyFont.Item1, bodyFont.Item2, 4));
// Picture
double width = widthWithMargins / 3 * 2, height = 0;
var mountain = GetImage(ref mako, @"..\..\..\..\Images\Mountain.jpg", ref width, ref height);
paragraphs[paraIndex].addRun(ILayoutImageRun.create(mountain, width, height));
for (uint paraCopyIndex = 3; paraCopyIndex < 8; paraCopyIndex++)
{
paragraphs.append(CreateParagraph(7, ILayoutParagraph.eHorizontalAlignment.eHAJustified, 1.2));
paragraphs[++paraIndex].addRun(
ILayoutTextRun.create(paragraphCopy[paraCopyIndex], bodyFont.Item1, bodyFont.Item2, 14.0));
}
var david = GetOpenTypeFont(mako, new List<string>(new[] { "DavidRegular" }));
var arial = GetOpenTypeFont(mako, new List<string>(new[] { "Arial" }));
// Right-to-left language test
string david_header = "סביבות מגורים - מהביוספירה ועד לנישה האקולוגית";
string david_para =
"מרכזו של כדור-הארץ נמצא כמעט 6400 קילומטר מתחת לפני הקרקע. עד כה הצליח האדם להגיע רק לעומק של שמונה קילומטרים בקירוב. אולם כבר היום ידוע לנו כי קילומטרים ספורים מתחת לכפות רגלינו כבר עולה טמפרטורת הארץ למידה שאיננה מאפשרת קיום של חיים. קילומטרים ספורים מעל לראשנו האוויר נעשה דליל וקר, ולא מתאפשר בו קיום של חיים. בתווך מצויה הביוספירה (ביו=חיים, ספירה=עולם) ¬– שכבה דקה על פני כדור-הארץ שעובייה כקליפת תפוח-העץ ביחס לתפוח השלם, אשר בה מתקיים כל עושר החיים המוכר לנו. הביוספירה כוללת את מכלול החיים בים וביבשה, במחילות ובין הרגבים שמתחת לפני הקרקע ובשכבות האוויר הסמוכות לקרקע. הביוספירה היא סביבת החיים הגדולה ביותר המוכרת לנו.";
paragraphs.append(CreateParagraph(8, ILayoutParagraph.eHorizontalAlignment.eHARight));
paragraphs[++paraIndex].addRun(ILayoutTextRun.create(david_header, david.Item1, david.Item2, 16.0));
paragraphs.append(CreateParagraph(6.5, ILayoutParagraph.eHorizontalAlignment.eHARight));
paragraphs[++paraIndex].addRun(ILayoutTextRun.create(david_para, arial.Item1, arial.Item2, 13.0));
// Add to the page
fixedPage.appendChild(layout.layout(paragraphs));
// Write PDF
using var output = IPDFOutput.create(mako);
output.setParameter("maxAccumulatedPages", "1"); // MAKOSUP-11222
output.setParameter("Producer", "Mako Layout Engine");
output.writeAssembly(assembly, "MyFirstLayout(CS).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}");
}
}
public static double PT2XPS(double value)
{
return value / 72.0 * 96.0;
}
public static double MM2XPS(double value)
{
return value / 25.4 * 96.0;
}
// Get font
static Tuple<IDOMFontOpenType, uint> GetOpenTypeFont(IJawsMako mako, List<string> fontsToTry)
{
// Pick a font
IDOMFont font = null!;
uint fontIndex = 0; // In case the font is inside a TrueType collection
foreach (var fontToTry in fontsToTry)
{
try
{
font = mako.findFont(fontToTry, out fontIndex);
break;
}
catch (MakoException)
{
// Bad or missing font - default to Arial
font = mako.findFont("Arial", out fontIndex);
}
}
if (font!.getFontType() == IDOMFont.eFontType.eFontTypeOpenType)
return new Tuple<IDOMFontOpenType, uint>(IDOMFontOpenType.fromRCObject(font), fontIndex);
return new Tuple<IDOMFontOpenType, uint>(null!, 0);
}
// Draw a path using a solid color brush with a thickness of 1
private static IDOMPathNode DrawFrame(IJawsMako mako, FRect frameBounds)
{
double margin = 4;
using var border = new FRect(frameBounds.x - margin, frameBounds.y - margin, frameBounds.dX + margin * 2,
frameBounds.dY + margin * 2);
using var solidBrush = IDOMSolidColorBrush.createSolidRgb(mako, 0.8f, 0.8f, 0.8f);
var path = IDOMPathNode.createStroked(mako, IDOMPathGeometry.create(mako, border), solidBrush);
path.setStrokeThickness(1);
return path;
}
// Add a layout, draw an outline if requested
private static void AddFrame(ref IJawsMako mako, ref ILayout layout, ref IDOMFixedPage fixedPage, FRect positionAndSize, bool drawOutline = false)
{
layout.addFrame(ILayoutFrame.create(positionAndSize));
// Draw a box where the frame is going to be placed
if (drawOutline)
fixedPage.appendChild(DrawFrame(mako, positionAndSize));
}
// Create paragraph
private static ILayoutParagraph CreateParagraph(
double spaceAfter = 0.0,
ILayoutParagraph.eHorizontalAlignment justification = ILayoutParagraph.eHorizontalAlignment.eHALeft,
double leading = 1.0,
double spaceBefore = 0.0)
{
var templateParagraph = ILayoutParagraph.create(justification);
if (spaceAfter > 0.0)
templateParagraph.setSpacing(spaceAfter);
if (spaceBefore > 0.0)
templateParagraph.setSpacing(spaceBefore, true);
templateParagraph.setLeading(leading);
return templateParagraph;
}
// 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;
}
}
/* --------------------------------------------------------------------------------
* <copyright file="LayoutExample.java" company="Global Graphics Software Ltd">
* Copyright (c) 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>
* ---------------------------------------------------------------------------------
*/
package com.globalgraphics;
import java.util.ArrayList;
import com.globalgraphics.JawsMako.jawsmakoIF.*;
public class LayoutExample
{
public static void main(String[] args)
{
try
{
var mako = IJawsMako.create();
IJawsMako.enableAllFeatures(mako);
IEDLClassFactory factory = mako.getFactory ();
// Get a page ready to accept some DOM
var assembly = IDocumentAssembly.create(mako);
var document = IDocument.create(mako);
assembly.appendDocument(document);
var page = IPage.create(mako);
document.appendPage(page);
var fixedPage = IDOMFixedPage.create(factory);
page.setContent(fixedPage);
// Create a layout
var layout = ILayout.create(mako);
// Add some frames to hold content
boolean drawBorder = false;
var margin = MM2XPS(12);
var widthWithMargins = fixedPage.getWidth() - margin * 2;
AddFrame(mako, layout, fixedPage, new FRect(margin, margin, widthWithMargins, MM2XPS(40)), drawBorder); // Banner
AddFrame(mako, layout, fixedPage, new FRect(margin, margin + MM2XPS(40), widthWithMargins / 3 - MM2XPS(2), MM2XPS(83)), drawBorder); // Sidebar with 2mm right margin
AddFrame(mako, layout, fixedPage, new FRect(margin + widthWithMargins / 3, margin + MM2XPS(40), widthWithMargins / 3 * 2, MM2XPS(83)), drawBorder); // Pic
AddFrame(mako, layout, fixedPage, new FRect(margin, margin + MM2XPS(125), widthWithMargins, MM2XPS(155)), drawBorder);
// Get a font
var titleFont = new GetOpenTypeFont(mako, new ArrayList<>() {{ add("Arial Black"); }});
var bodyFont = new GetOpenTypeFont(mako, new ArrayList<>() {{ add("DejaVu Sans Book"); add("Gill Sans"); add("Arial"); }});
var bodyBold = new GetOpenTypeFont(mako, new ArrayList<>() {{ add("DejaVu Sans Book Bold"); add("Gill Sans Bold"); add("Arial Bold"); }});
// Create a colour
var darkBlue = IDOMColor.createSolidRgb(mako.getFactory(), 0.0f, 0.0f, 0.5f);
var paragraphCopy = new ArrayList<String>();
paragraphCopy.add("Travel Blog");
paragraphCopy.add("\nThe beauty of where I found myself inspired me to write this.");
paragraphCopy.add("As the sun rose over the horizon, casting a warm golden hue across the landscape, a breathtaking scene unfolded before the onlooker's eyes. Standing at the water's edge, one's gaze extended out over a pristine lake that shimmered like a sheet of glass, reflecting the majestic beauty that surrounded it. The lake seemed to hold its breath, mirroring with utmost precision the awe-inspiring sight that lay just beyond its tranquil surface.");
paragraphCopy.add("Stretching magnificently into the distance, a range of rocky mountains dominated the backdrop. Each peak soared towards the heavens, their rugged surfaces etched by the passage of time and the forces of nature. The mountains stood resolute, a testament to the immense power and grandeur of the natural world. Their colors shifted subtly, painted with a breathtaking array of earthy tones \u2014 from deep siennas and ochres to soft grays and greens \u2014 all framed by the azure expanse of the sky.");
paragraphCopy.add("The sky itself seemed to be a canvas of its own, an ever-changing masterpiece of color and light. Towering cumulus clouds danced gracefully, casting dramatic shadows that gently caressed the mountains' slopes. The fluffy white clouds looked as though they were soft pillows, inviting the weary soul to rest upon their tender embrace. The sky stretched boundlessly, seeming to touch the very edges of the earth, a reminder of the vastness of the universe and the limitless possibilities that lay beyond the human imagination.");
paragraphCopy.add("As a gentle breeze whispered through the air, ripples formed across the surface of the lake, momentarily distorting the mirror-like reflection. The tiny waves moved in rhythmic harmony, lending an animated quality to the otherwise still waters. With each passing gust, the mountains appeared to ripple across the lake's surface, as though a magnificent magic spell had been cast upon the land.");
paragraphCopy.add("In the distance, a lone boat glided silently across the lake, its presence adding a touch of serenity to the already tranquil scene. The boat's wake created a delicate trail on the water, a fleeting mark of human existence in the midst of the grandeur of nature. It served as a reminder of the delicate balance between mankind and the Earth, and how nature's beauty can be both admired and preserved.");
paragraphCopy.add("Birds soared overhead, their graceful silhouettes weaving intricate patterns against the sky. They navigated the currents with effortless grace, adding life to the enchanting tableau. The soft cries of the birds mixed harmoniously with the gentle lapping of the lake's water against the shore, creating a soothing symphony that echoed through the air.");
paragraphCopy.add("");
// A vector to point to each paragraph to be added to the frame(s)
CEDLVectILayoutParagraph paragraphs = new CEDLVectILayoutParagraph();
long paraIndex = 0;
// Create paragraphs and add text runs to them
// Title
paragraphs.append(ILayoutParagraph.create(ILayoutParagraph.eHorizontalAlignment.eHACenter));
var run = ILayoutTextRun.create(paragraphCopy.get(0), titleFont.font, titleFont.index, PT2XPS(60));
run.setColor(darkBlue);
paragraphs.getitem(paraIndex).addRun(run);
// Intro
paragraphs.append(CreateParagraph(9, ILayoutParagraph.eHorizontalAlignment.eHAJustified, 1.3, 0.0));
paragraphs.getitem(++paraIndex).addRun(
ILayoutTextRun.create(paragraphCopy.get(1), bodyBold.font, bodyBold.index, 14.0));
// Sidebar
paragraphs.append(CreateParagraph(7, ILayoutParagraph.eHorizontalAlignment.eHAJustified, 1.2, 0.0));
paragraphs.getitem(++paraIndex).addRun(
ILayoutTextRun.create(paragraphCopy.get(2), bodyFont.font, bodyFont.index, 14.0));
// Break to next frame
paragraphs.append(ILayoutParagraph.create());
paragraphs.getitem(++paraIndex).addRun(ILayoutTextRun.create("\n", bodyFont.font, bodyFont.index, 4));
// Picture
double width = widthWithMargins / 3 * 2, height = 0;
var mountain = new GetImage(mako, "../Images/Mountain.jpg", width, height);
paragraphs.getitem(paraIndex).addRun(ILayoutImageRun.create(mountain.image, mountain.width, mountain.height));
for (int paraCopyIndex = 3; paraCopyIndex < 8; paraCopyIndex++)
{
paragraphs.append(CreateParagraph(7.0, ILayoutParagraph.eHorizontalAlignment.eHAJustified, 1.2, 0.0));
paragraphs.getitem(++paraIndex).addRun(
ILayoutTextRun.create(paragraphCopy.get(paraCopyIndex), bodyFont.font, bodyFont.index, 14.0));
}
var david = new GetOpenTypeFont(mako, new ArrayList<>() {{ add( "DavidRegular"); }});
var arial = new GetOpenTypeFont(mako, new ArrayList<>() {{ add( "Arial"); }});
// Right-to-left language test
String heb_header = "סביבות מגורים - מהביוספירה ועד לנישה האקולוגית";
String heb_para =
"מרכזו של כדור-הארץ נמצא כמעט 6400 קילומטר מתחת לפני הקרקע. עד כה הצליח האדם להגיע רק לעומק של שמונה קילומטרים בקירוב. אולם כבר היום ידוע לנו כי קילומטרים ספורים מתחת לכפות רגלינו כבר עולה טמפרטורת הארץ למידה שאיננה מאפשרת קיום של חיים. קילומטרים ספורים מעל לראשנו האוויר נעשה דליל וקר, ולא מתאפשר בו קיום של חיים. בתווך מצויה הביוספירה (ביו=חיים, ספירה=עולם) ¬– שכבה דקה על פני כדור-הארץ שעובייה כקליפת תפוח-העץ ביחס לתפוח השלם, אשר בה מתקיים כל עושר החיים המוכר לנו. הביוספירה כוללת את מכלול החיים בים וביבשה, במחילות ובין הרגבים שמתחת לפני הקרקע ובשכבות האוויר הסמוכות לקרקע. הביוספירה היא סביבת החיים הגדולה ביותר המוכרת לנו.";
paragraphs.append(CreateParagraph(8, ILayoutParagraph.eHorizontalAlignment.eHARight, 1.0, 0.0));
paragraphs.getitem(++paraIndex).addRun(ILayoutTextRun.create(heb_header, david.font, david.index, 16.0));
paragraphs.append(CreateParagraph(6.5, ILayoutParagraph.eHorizontalAlignment.eHARight, 1.0, 0.0));
paragraphs.getitem(++paraIndex).addRun(ILayoutTextRun.create(heb_para, arial.font, arial.index, 13.0));
// Add to the page
fixedPage.appendChild(layout.layout(paragraphs));
// Write PDF
var output = IPDFOutput.create(mako);
output.setParameter("Producer", "Mako Layout Engine");
output.writeAssembly(assembly, "MyFirstLayout(Java).pdf");
// Done
} catch (Exception e) {
e.printStackTrace();
}
}
public static double PT2XPS(double value)
{
return value / 72.0 * 96.0;
}
public static double MM2XPS(double value)
{
return value / 25.4 * 96.0;
}
// Draw a path using a solid color brush with a thickness of 1
private static IDOMPathNode DrawFrame(IEDLClassFactory factory, FRect frameBounds)
{
double margin = 4;
var border = new FRect(frameBounds.getX() - margin, frameBounds.getY() - margin,
frameBounds.getDX() + margin * 2,frameBounds.getDY() + margin * 2);
var solidBrush = IDOMSolidColorBrush.createSolidRgb(factory, 0.8f, 0.8f, 0.8f);
var path = IDOMPathNode.createStroked(factory, IDOMPathGeometry.create(factory, border), solidBrush);
path.setStrokeThickness(1);
return path;
}
// Add a layout, draw an outline if requested
private static void AddFrame(IJawsMako mako, ILayout layout, IDOMFixedPage fixedPage, FRect positionAndSize, boolean drawOutline)
{
layout.addFrame(ILayoutFrame.create(positionAndSize));
// Draw a box where the frame is going to be placed
if (drawOutline)
fixedPage.appendChild(DrawFrame(mako.getFactory(), positionAndSize));
}
// Create paragraph
private static ILayoutParagraph CreateParagraph(
double spaceAfter,
ILayoutParagraph.eHorizontalAlignment justification,
double leading,
double spaceBefore)
{
var templateParagraph = ILayoutParagraph.create(justification);
if (spaceAfter > 0.0)
templateParagraph.setSpacing(spaceAfter);
if (spaceBefore > 0.0)
templateParagraph.setSpacing(spaceBefore, true);
templateParagraph.setLeading(leading);
return templateParagraph;
}
}
GetOpenTypeFont
/* --------------------------------------------------------------------------------
* <copyright file="GetImage.java" company="Global Graphics Software Ltd">
* Copyright (c) 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>
* ---------------------------------------------------------------------------------
*/
package com.globalgraphics;
import com.globalgraphics.JawsMako.jawsmakoIF.IDOMFontOpenType;
import com.globalgraphics.JawsMako.jawsmakoIF.IJawsMako;
import java.util.ArrayList;
public class GetOpenTypeFont {
private IJawsMako mako;
public IDOMFontOpenType font;
public long index;
public GetOpenTypeFont(IJawsMako mako, ArrayList<String> fontsToTry) {
long[] fontIndex = new long[1]; // In case the font is inside a TrueType collection
for (String fontToTry : fontsToTry) {
try {
font = IDOMFontOpenType.fromRCObject(mako.findFont(fontToTry, fontIndex));
break;
} catch (Exception e) {
// Bad or missing font - default to Arial
font = IDOMFontOpenType.fromRCObject(mako.findFont("Arial", fontIndex));
}
}
index = fontIndex[0];
}
}
GetImage
/* --------------------------------------------------------------------------------
* <copyright file="GetImage.java" company="Global Graphics Software Ltd">
* Copyright (c) 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>
* ---------------------------------------------------------------------------------
*/
package com.globalgraphics;
import com.globalgraphics.JawsMako.jawsmakoIF.*;
import java.io.File;
public class GetImage {
public IDOMImage image;
public double width;
public double height;
GetImage(IJawsMako mako, String imageFile, double requiredWidth, double requiredHeight) throws Exception {
File fileInfo = new File(imageFile);
if (!fileInfo.exists()) {
throw new Exception("Image file {imageFile} not found.");
}
image = null;
if (fileInfo.getName().toLowerCase().contains(".jpg")) {
image = IDOMJPEGImage.create(mako.getFactory(), IInputStream.createFromFile(mako.getFactory(), imageFile));
}
if (fileInfo.getName().toLowerCase().contains(".png")) {
image = IDOMPNGImage.create(mako.getFactory(), IInputStream.createFromFile(mako.getFactory(), imageFile));
}
if (fileInfo.getName().toLowerCase().contains(".tif")) {
image = IDOMTIFFImage.create(mako.getFactory(), IInputStream.createFromFile(mako.getFactory(), imageFile));
}
if (image == null) {
throw new Exception("Image file " + imageFile + " could not be loaded.");
}
var frame = image.getImageFrame(mako.getFactory());
double imageWidth = frame.getWidth();
double imageHeight = frame.getHeight();
double aspectRatio = imageWidth / imageHeight;
// If requested dimensions are both zero, return the actual size
if (requiredWidth == 0.0 && requiredHeight == 0.0) {
width = imageWidth;
height = imageHeight;
}
// If requested dimensions are both non-zero, return those values (do nothing)
if (requiredWidth > 0.0 && requiredHeight > 0.0) {
width = requiredWidth;
height = requiredHeight;
}
// Otherwise, calculate the missing dimension
if (requiredHeight == 0.0) {
width = requiredWidth;
height = requiredWidth / aspectRatio;
} else {
width = requiredHeight * aspectRatio;
height = requiredHeight;
}
}
}