Skip to content

Commit

Permalink
close #249: OOM due to font loading. Fonts are not cached, loading a …
Browse files Browse the repository at this point in the history
…single instance of the same font in memory, per document.
  • Loading branch information
ediweissmann committed Sep 12, 2016
1 parent d3fbb3a commit 7d6dd27
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ public enum UnicodeType0Font implements FontResource {
this.resource = resource;
}

@Override
public String getResource() {
return resource;
}

@Override
public InputStream getFontStream() {
return this.getClass().getResourceAsStream(resource);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
* @author Andrea Vacondio
*/
public interface FontResource {
/**
* @return Unique identifier
*
*/
String getResource();
/**
* @return the stream where the font is read from
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ public enum OptionalUnicodeType0Font implements FontResource {
this.resource = resource;
}

@Override
public String getResource() {
return resource;
}

@Override
public InputStream getFontStream() {
return this.getClass().getResourceAsStream(resource);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.sejda.impl.sambox.component.PageGeometricalShapeWriter;
import org.sejda.impl.sambox.component.PageImageWriter;
import org.sejda.impl.sambox.component.PageTextWriter;
import org.sejda.impl.sambox.util.FontUtils;
import org.sejda.model.RectangularBox;
import org.sejda.model.exception.TaskException;
import org.sejda.model.input.PdfSource;
Expand Down Expand Up @@ -196,6 +197,8 @@ public void execute(EditParameters parameters) throws TaskException {
nameRequest().originalName(source.getName()).fileNumber(currentStep));
outputWriter.addOutput(file(tmpFile).name(outName));

FontUtils.clearLoadedFontCache(documentHandler.getUnderlyingPDDocument());

notifyEvent(executionContext().notifiableTaskMetadata()).stepsCompleted(currentStep).outOf(totalSteps);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.sejda.impl.sambox.component.DefaultPdfSourceOpener;
import org.sejda.impl.sambox.component.PDDocumentHandler;
import org.sejda.impl.sambox.component.SetHeaderFooterWriter;
import org.sejda.impl.sambox.util.FontUtils;
import org.sejda.model.exception.TaskException;
import org.sejda.model.input.PdfSource;
import org.sejda.model.input.PdfSourceOpener;
Expand Down Expand Up @@ -95,6 +96,7 @@ public void execute(SetHeaderFooterParameters parameters) throws TaskException {
footerWriter.write(parameters, currentFileCounter, outName);
documentHandler.savePDDocument(tmpFile);
outputWriter.addOutput(file(tmpFile).name(outName));
FontUtils.clearLoadedFontCache(documentHandler.getUnderlyingPDDocument());
}

notifyEvent(executionContext().notifiableTaskMetadata()).stepsCompleted(currentStep).outOf(totalSteps);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.io.InputStream;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;

Expand Down Expand Up @@ -100,11 +101,34 @@ public static PDFont fontOrFallback(String text, PDFont font, Supplier<PDFont> f
return font;
}

// caches fonts, PER DOCUMENT
// has no auto-magical way to clear the cache when doc processing is done
// if you use this in a long lived process, call the cache clear method to avoid leaking memory
private static Map<PDDocument, Map<String, PDFont>> loadedFontCache = new HashMap<>();

public static void clearLoadedFontCache() {
loadedFontCache.clear();
}

public static void clearLoadedFontCache(PDDocument document) {
loadedFontCache.remove(document);
}

private static PDFont loadFont(PDDocument document, FontResource font) {
if(!loadedFontCache.containsKey(document)){
loadedFontCache.put(document, new HashMap<>());
}

Map<String, PDFont> docCache = loadedFontCache.get(document);
if(docCache.containsKey(font.getResource())){
return docCache.get(font.getResource());
}

InputStream in = font.getFontStream();
try {
PDType0Font loaded = PDType0Font.load(document, in);
LOG.trace("Loaded font {}", loaded.getName());
docCache.put(font.getResource(), loaded);
return loaded;
} catch (IOException e) {
LOG.warn("Failed to load font " + font, e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
*/
package org.sejda.impl.sambox.util;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
Expand All @@ -31,6 +33,7 @@
import org.junit.Test;
import org.sejda.model.pdf.StandardType1Font;
import org.sejda.sambox.pdmodel.PDDocument;
import org.sejda.sambox.pdmodel.font.PDFont;
import org.sejda.sambox.pdmodel.font.PDType1Font;

/**
Expand Down Expand Up @@ -89,4 +92,13 @@ public void testFontOrFallbackNullSipplier() {
PDType1Font expected = getStandardType1Font(StandardType1Font.CURIER);
assertEquals(expected, fontOrFallback("कसौटी", expected, null));
}

@Test
public void testCaching() {
PDDocument doc = new PDDocument();
PDFont expected = FontUtils.findFontFor(doc, "ทดสอบ");

PDFont actual = findFontFor(doc, "ทด");
assertTrue("Font is cached, same instance is returned", expected == actual);
}
}

0 comments on commit 7d6dd27

Please sign in to comment.