Configuring fonts for AWS Lambda

Problem

We have Aspose.Total for Java. Currently we are running on java8, which is ran by default on Amazon Linux 1.

Soon, they will be migrating people over to use corretto Announcing migration of the Java 8 runtime in AWS Lambda to Amazon Corretto | AWS Compute Blog

When changing our lambda to use java8.al2 (which is corretto on Amazon Linux 2), using the Aspose libraries on a file (to generate a PDF) does the following:

Exception in thread "main" java.lang.NullPointerException
        at sun.awt.FontConfiguration.getVersion(FontConfiguration.java:1264)
        at sun.awt.FontConfiguration.readFontConfigFile(FontConfiguration.java:219)
        at sun.awt.FontConfiguration.init(FontConfiguration.java:107)
        at sun.awt.X11FontManager.createFontConfiguration(X11FontManager.java:774)
        at sun.font.SunFontManager$2.run(SunFontManager.java:441)
        at java.security.AccessController.doPrivileged(Native Method)
        at sun.font.SunFontManager.<init>(SunFontManager.java:386)
        at sun.awt.FcFontManager.<init>(FcFontManager.java:35)
        at sun.awt.X11FontManager.<init>(X11FontManager.java:57)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
        at java.lang.Class.newInstance(Class.java:442)
        at sun.font.FontManagerFactory$1.run(FontManagerFactory.java:83)
        at java.security.AccessController.doPrivileged(Native Method)
        at sun.font.FontManagerFactory.getInstance(FontManagerFactory.java:74)
        at java.awt.Font.getFont2D(Font.java:491)
        at java.awt.Font.access$000(Font.java:224)
        at java.awt.Font$FontAccessImpl.getFont2D(Font.java:228)
        at sun.font.FontUtilities.getFont2D(FontUtilities.java:180)
        at sun.java2d.SunGraphics2D.checkFontInfo(SunGraphics2D.java:669)
        at sun.java2d.SunGraphics2D.getFontInfo(SunGraphics2D.java:830)
        at sun.java2d.SurfaceData.getTextPipe(SurfaceData.java:754)
        at sun.java2d.SurfaceData.validatePipe(SurfaceData.java:715)
        at sun.java2d.SunGraphics2D.validatePipe(SunGraphics2D.java:446)
        at sun.java2d.pipe.ValidatePipe.validate(ValidatePipe.java:55)
        at sun.java2d.pipe.ValidatePipe.fill(ValidatePipe.java:159)
        at sun.java2d.SunGraphics2D.fill(SunGraphics2D.java:2527)
        at com.aspose.slides.internal.lk.do.do(Unknown Source)
        at com.aspose.slides.internal.lk.case.do(Unknown Source)
        at com.aspose.slides.internal.lk.do.do(Unknown Source)
        at com.aspose.slides.internal.lk.do.new(Unknown Source)
        at com.aspose.slides.internal.lk.do.do(Unknown Source)
        at com.aspose.slides.internal.li.new.do(Unknown Source)
        at com.aspose.slides.internal.li.new.do(Unknown Source)
        at com.aspose.slides.internal.li.do.do(Unknown Source)
        at com.aspose.slides.internal.li.new.do(Unknown Source)
        at com.aspose.slides.internal.f1.goto.do(Unknown Source)
        at com.aspose.slides.protected.do(Unknown Source)
        at com.aspose.slides.protected.do(Unknown Source)
        at com.aspose.slides.Slide.do(Unknown Source)
        at com.aspose.slides.Slide.do(Unknown Source)
        at com.aspose.slides.Slide.do(Unknown Source)
        at com.aspose.slides.af5.do(Unknown Source)
        at com.aspose.slides.af5.do(Unknown Source)
        at com.aspose.slides.Presentation.do(Unknown Source)
        at com.aspose.slides.Presentation.save(Unknown Source)
        at com.teamnorthwoods.Main.main(Main.java:14)

After some digging, Broken Font support (Fontconfig) in AWS-Lambda (java11) · Issue #118 · corretto/corretto-11 · GitHub mentions that Amazon Linux 2 does not have fontconfig in it, but to get around it temporarily (they mention in the thread) you can mount a lambda layer for fontconfig.zip and that should work.

In doing so, while it does get rid of the error above and generated a PDF, the output is missing fonts.

Here is the code that I’m using to produce at least this problem:

public class Main {
    public static void main(String[] args) {
        String file = args.length > 0 ? args[0] : "festivus_trivia.pptx";
        new License().setLicense(getLicense());
        Presentation presentation = new Presentation(file);
        presentation.save(file + ".pdf", SaveFormat.Pdf);
    } 
}

Attached is the file that can be used to reproduce.

festivus_trivia.zip (220.9 KB)

amazon/aws-lambda-java:8.al2

If I do the following

docker run --rm -it --entrypoint=/bin/bash -v $PWD:/var/task amazon/aws-lambda-java:8.al2

Once I get in there, I can reproduce the initial error with this:

java -cp my.jar:./* com.teamnorthwoods.Main

As I said above, this is because it doesn’t have fontconfig.

After running this, I can get it to work:

yum install -y fontconfig

Questions

  • what is wrong with fontconfig.zip that is referenced on that issue thread above? It seems to get past the initial problem, but the output doesn’t have some of the fonts
  • how can I get useful information out of Aspose when it’s unable to find fonts? Without understanding what it’s missing, it’s hard to troubleshoot what is there.

Here is the fontconfig.zip layer that still does not work, but gets past the error at least.

fontconfig.zip (3.4 MB)

Note that I added a log4j.properties with the following:

log4j.rootLogger = DEBUG, console

log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.target=System.out
log4j.appender.console.immediateFlush=true
log4j.appender.console.encoding=UTF-8
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.conversionPattern=%m%n

log4j.logger.com.aspose.slides=DEBUG

But I don’t see anything coming from Aspose.Slides. Is there any way to turn on logging for the library so I can tel what it’s failing to do?

@leviwilson,
Thank you for posting the query. It will take me a while to investigate the issue. I will answer you as soon as possible.

Thanks. For reference, I have ran into similar issues before with Aspose.PDF in an AWS environment, and this feels similar in at least the sense that the Aspose libraries I’m not sure are honoring what fc-list says is available on the system?

Here is the other issue that I’d ran into trouble with before: Aspose.PDF / AWS Lambda C# runtime

In this situation as well, everything looks good with regard to fc-list and it does produce a PDF, but the fonts are missing and the library doesn’t:

  • indicate that there was a problem
  • provide any logging / debug information (that I can tell) about what might be missing in the environment

@leviwilson,
Thank you for the additional information. I will answer you later.

@leviwilson,
Please try the following:

  1. Use the latest version of Aspose.Slides.
  2. Install the fonts you need in your S3 storage (including Curlz MT font you have in your presentation).
  3. Load external fonts as shown below:
// for example
String [] nFonts =
{ 
    "arialunicode.ttf", "Arial Narrow.ttf", "Arial Narrow Italic.ttf",
    "ariali.ttf", "Calibri.ttf", "Arial.ttf" 
};

for(String fontName : nFonts)
{
    S3Object fFile = client.getObject(bucketName, fontName);
    try {
        InputStream is = fFile.getObjectContent();
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        int nRead;
        byte[] data = new byte[4096];
        while ((nRead = is.read(data, 0, data.length)) != -1) {
          buffer.write(data, 0, nRead);
        }
        FontsLoader.loadExternalFont(buffer.toByteArray());
        buffer.close();
    } catch (IOException e2) {
        context.getLogger().log(e2.getMessage());
    }
}

Also, you can implement IWarningCallback interface and detect font substitutions as shown below:

LoadOptions loadOptions = new LoadOptions();
loadOptions.setWarningCallback(new LoadWarningCallback());

Presentation pres = new Presentation(dataPath + "Testing.pptx");

PdfOptions pdfOptions = new PdfOptions();
pdfOptions.setWarningCallback(new PdfWarningCallback());

pres.save(dataPath + "Testing_out.pdf", SaveFormat.Pdf, pdfOptions);

Documents: Getting Warning Callbacks for Fonts Substitution in Aspose.Slides
API Reference: IWarningCallback interface

Useful articles: PowerPoint Fonts

I will look into turning the logging on. I’m not particularly interested in loading fonts from S3 but rather I’d like to understand why Aspose does not seem to pick up fonts that say they are available in fc-list in the two examples I’ve provided you.

@leviwilson,
Usually, fonts will not load for two reasons in Aspose.Slides:

  • font doesn’t exist in OS
  • font file or font folder is not available

You should programmatically check for these cases and font substitution.