org.verapdf.pd.font.PDCIDFont Maven / Gradle / Ivy
Show all versions of parser Show documentation
/**
* This file is part of veraPDF Parser, a module of the veraPDF project.
* Copyright (c) 2015, veraPDF Consortium
* All rights reserved.
*
* veraPDF Parser is free software: you can redistribute it and/or modify
* it under the terms of either:
*
* The GNU General public license GPLv3+.
* You should have received a copy of the GNU General Public License
* along with veraPDF Parser as the LICENSE.GPL file in the root of the source
* tree. If not, see http://www.gnu.org/licenses/ or
* https://www.gnu.org/licenses/gpl-3.0.en.html.
*
* The Mozilla Public License MPLv2+.
* You should have received a copy of the Mozilla Public License along with
* veraPDF Parser as the LICENSE.MPL file in the root of the source tree.
* If a copy of the MPL was not distributed with this file, you can obtain one at
* http://mozilla.org/MPL/2.0/.
*/
package org.verapdf.pd.font;
import org.verapdf.as.ASAtom;
import org.verapdf.as.io.ASInputStream;
import org.verapdf.cos.*;
import org.verapdf.pd.font.cff.CFFFontProgram;
import org.verapdf.pd.font.cff.CFFType1FontProgram;
import org.verapdf.pd.font.cmap.CMap;
import org.verapdf.pd.font.opentype.OpenTypeFontProgram;
import org.verapdf.pd.font.truetype.CIDFontType2Program;
import org.verapdf.tools.FontProgramIDGenerator;
import org.verapdf.tools.StaticResources;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Class represents CIDFont on PD level.
*
* @author Sergey Shemyakov
*/
public class PDCIDFont extends PDFont {
private static final Logger LOGGER = Logger.getLogger(PDCIDFont.class.getCanonicalName());
private static final Double DEFAULT_CID_FONT_WIDTH = Double.valueOf(1000d);
protected CMap cMap;
private CIDWArray widths;
private PDCIDSystemInfo cidSystemInfo;
/**
* Constructor from COSDictionary and CMap with code -> cid mapping.
*
* @param dictionary is COSDictionary of CIDFont.
* @param cMap is CMap object containing mapping code -> cid.
*/
public PDCIDFont(COSDictionary dictionary, CMap cMap) {
super(dictionary);
this.cMap = cMap;
}
/**
* Constructor that sets font program for this CIDFont. Can be used when
* font program should not be parsed twice and is already read.
*
* @param dictionary is COSDictionary of CIDFont.
* @param cMap is CMap object containing mapping code -> cid.
* @param fontProgram is embedded font program associated with this CIDFont.
* @param isFontParsed is true if embedded font program has been already
* parsed.
*/
public PDCIDFont(COSDictionary dictionary, CMap cMap, FontProgram fontProgram,
boolean isFontParsed) {
this(dictionary, cMap);
this.fontProgram = fontProgram;
this.isFontParsed = isFontParsed;
if (fontProgram != null) {
this.isFontParsed = true;
}
}
/*
Do not forget to set cMap!!!
*/
protected PDCIDFont(COSDictionary dictionary) {
super(dictionary);
}
/**
* @return a stream identifying which CIDs are present in the CIDFont file.
*/
public COSStream getCIDSet() {
COSObject cidSet = this.fontDescriptor.getKey(ASAtom.CID_SET);
return cidSet == null ? null : (COSStream) cidSet.getDirectBase();
}
/**
* @return a specification of the mapping from CIDs to glyph indices if
* CIDFont is a Type 2 CIDFont.
*/
public COSObject getCIDToGIDMap() {
return this.dictionary.getKey(ASAtom.CID_TO_GID_MAP);
}
/**
* {@inheritDoc}
*/
@Override
public Double getWidth(int code) {
if (this.widths == null) {
COSObject w = this.dictionary.getKey(ASAtom.W);
if (w.empty() || w.getType() != COSObjType.COS_ARRAY) {
return getDefaultWidth();
}
this.widths = new CIDWArray((COSArray) w.getDirectBase());
}
Double res = widths.getWidth(this.cMap.toCID(code));
if (res == null) {
res = getDefaultWidth();
}
return res;
}
/**
* {@inheritDoc}
*/
@Override
public Double getDefaultWidth() {
COSObject dw = this.dictionary.getKey(ASAtom.DW);
if (dw.getType().isNumber()) {
return dw.getReal();
} else {
return DEFAULT_CID_FONT_WIDTH;
}
}
/**
* {@inheritDoc}
*/
@Override
public int readCode(InputStream stream) throws IOException {
if (cMap != null) {
return cMap.getCodeFromStream(stream);
}
throw new IOException("No CMap for Type 0 font " +
(this.getName() == null ? "" : this.getName()));
}
/**
* {@inheritDoc}
*/
@Override
public FontProgram getFontProgram() {
if (!this.isFontParsed) {
this.isFontParsed = true;
if (fontDescriptor.canParseFontFile(ASAtom.FONT_FILE2) &&
this.getSubtype() == ASAtom.CID_FONT_TYPE2) {
COSStream trueTypeFontFile = fontDescriptor.getFontFile2();
COSKey key = trueTypeFontFile.getObjectKey();
COSObject cidToGIDMap = this.getCIDToGIDMap();
String fontProgramID = FontProgramIDGenerator.getCIDFontType2ProgramID(key, this.cMap, cidToGIDMap);
this.fontProgram = StaticResources.getCachedFont(fontProgramID);
if (fontProgram == null) {
try (ASInputStream fontData = trueTypeFontFile.getData(COSStream.FilterFlags.DECODE)) {
this.fontProgram = new CIDFontType2Program(
fontData, this.cMap, cidToGIDMap);
StaticResources.cacheFontProgram(fontProgramID, this.fontProgram);
} catch (IOException e) {
LOGGER.log(Level.FINE, "Can't read TrueType font program.", e);
}
}
} else if (fontDescriptor.canParseFontFile(ASAtom.FONT_FILE3)) {
COSStream fontFile = fontDescriptor.getFontFile3();
COSName subtype = (COSName) fontFile.getKey(ASAtom.SUBTYPE).getDirectBase();
COSKey key = fontFile.getObjectKey();
try {
boolean isSubset = this.isSubset();
if (ASAtom.CID_FONT_TYPE0C == subtype.getName()) {
String fontProgramID = FontProgramIDGenerator.getCFFFontProgramID(key, this.cMap, isSubset);
this.fontProgram = StaticResources.getCachedFont(fontProgramID);
if (fontProgram == null) {
try (ASInputStream fontData = fontFile.getData(COSStream.FilterFlags.DECODE)) {
this.fontProgram = new CFFFontProgram(fontData, this.cMap, isSubset);
StaticResources.cacheFontProgram(fontProgramID, this.fontProgram);
}
}
} else if (ASAtom.OPEN_TYPE == subtype.getName()) {
ASAtom fontName = ASAtom.getASAtom(this.getName());
boolean isCFF = fontName != ASAtom.TRUE_TYPE && fontName != ASAtom.CID_FONT_TYPE2;
boolean isSymbolic = this.isSymbolic();
COSObject encoding = this.getEncoding();
String fontProgramID = FontProgramIDGenerator.getOpenTypeFontProgramID(key, isCFF, isSymbolic, encoding, this.cMap, isSubset);
this.fontProgram = StaticResources.getCachedFont(fontProgramID);
if (fontProgram == null) {
try (ASInputStream fontData = fontFile.getData(COSStream.FilterFlags.DECODE)) {
this.fontProgram = new OpenTypeFontProgram(
fontData, isCFF, isSymbolic, encoding,
this.cMap, isSubset);
StaticResources.cacheFontProgram(fontProgramID, this.fontProgram);
}
}
}
} catch (IOException e) {
LOGGER.log(Level.FINE, "Can't read font program.", e);
}
} else {
this.fontProgram = null;
}
}
return this.fontProgram;
}
@Override
public float getWidthFromProgram(int code) {
int cid = this.cMap.toCID(code);
FontProgram font = getFontProgram();
CFFType1FontProgram cffType1 = CFFType1FontProgram.getCFFType1(font);
if (cid != 0 && cffType1 != null) {
// In this case we ignore internal notations of names from CFF
// Type 1 font and use external CMap
return cffType1.getWidthFromGID(cid);
}
return font.getWidth(code);
}
@Override
public boolean glyphIsPresent(int code) {
int cid = this.cMap.toCID(code);
FontProgram font = getFontProgram();
CFFType1FontProgram cffType1 = CFFType1FontProgram.getCFFType1(font);
if (cid != 0 && cffType1 != null) {
// In this case we ignore internal notations of names from CFF
// Type 1 font and use external CMap
return cffType1.containsGID(cid);
}
return font.containsCode(code);
}
/**
* @return CID System Info object for this CIDFont.
*/
public PDCIDSystemInfo getCIDSystemInfo() {
if (this.cidSystemInfo != null) {
return this.cidSystemInfo;
} else {
this.cidSystemInfo =
new PDCIDSystemInfo(this.dictionary.getKey(ASAtom.CID_SYSTEM_INFO));
return this.cidSystemInfo;
}
}
}