org.jpedal.fonts.tt.TTGlyphs Maven / Gradle / Ivy
Show all versions of OpenViewerFX Show documentation
/*
* ===========================================
* Java Pdf Extraction Decoding Access Library
* ===========================================
*
* Project Info: http://www.idrsolutions.com
* Help section for developers at http://www.idrsolutions.com/support/
*
* (C) Copyright 1997-2017 IDRsolutions and Contributors.
*
* This file is part of JPedal/JPDF2HTML5
*
@LICENSE@
*
* ---------------
* TTGlyphs.java
* ---------------
*/
package org.jpedal.fonts.tt;
import java.util.Map;
import org.jpedal.PdfDecoderInt;
import org.jpedal.fonts.FontMappings;
import org.jpedal.fonts.StandardFonts;
import org.jpedal.fonts.glyph.*;
import org.jpedal.fonts.objects.FontData;
import org.jpedal.fonts.tt.hinting.TTVM;
import org.jpedal.utils.LogWriter;
public class TTGlyphs extends PdfJavaGlyphs {
protected int[] CIDToGIDMap;
float[] FontBBox = {0f, 0f, 1000f, 1000f};
boolean isCorrupted;
private CMAP currentCMAP;
private Post currentPost;
private Glyf currentGlyf;
private Hmtx currentHmtx;
private Hhea currentHhea;
private Head currentHead;
private FontFile2 fontTable;
//private Head currentHead;
//private Name currentName;
//private Maxp currentMapx;
private Loca currentLoca;
private TTVM vm;
//private Hhea currentHhea;
private CFF currentCFF;
//int glyphCount=0;
//assume TT and set to OTF further down
int type = StandardFonts.TRUETYPE;
private int unitsPerEm;
private boolean hasCFF;
private boolean isCID;
/**
* used by non type3 font
*/
@Override
public PdfGlyph getEmbeddedGlyph(final GlyphFactory factory, final String glyph, final float[][] Trm, int rawInt,
final String displayValue, final float currentWidth, final String key) {
final int id = rawInt;
if (hasGIDtoCID && (isIdentity() || currentCMAP == null) && CIDToGIDMap.length > rawInt) {
final int mappedValue = CIDToGIDMap[rawInt];
if (mappedValue > 0) {
rawInt = mappedValue;
}
}
/* flush cache if needed*/
if (Trm != null && (lastTrm[0][0] != Trm[0][0]) | (lastTrm[1][0] != Trm[1][0]) |
(lastTrm[0][1] != Trm[0][1]) | (lastTrm[1][1] != Trm[1][1])) {
lastTrm = Trm;
flush();
}
//either calculate the glyph to draw or reuse if alreasy drawn
PdfGlyph transformedGlyph2 = getEmbeddedCachedShape(id);
if (transformedGlyph2 == null) {
//use CMAP to get actual glyph ID
int idx = rawInt;
if ((!isCID || !isIdentity()) && currentCMAP != null) {
idx = currentCMAP.convertIndexToCharacterCode(glyph, rawInt);
}
//if no value use post to lookup
if (idx < 1) {
idx = currentPost.convertGlyphToCharacterCode(glyph);
}
//shape to draw onto
try {
if (hasCFF) {
transformedGlyph2 = currentCFF.getCFFGlyph(factory, glyph, Trm, idx, displayValue, currentWidth, key);
//set raw width to use for scaling
if (transformedGlyph2 != null) {
if (isCID && remappedCFFFont) {
idx = currentCMAP.getGlyphToIndex(rawInt);
}
//transformedGlyph2.setWidth(getUnscaledWidth(glyph, rawInt, false));
transformedGlyph2.setWidth(getUnscaledWidth(glyph, idx, false));
}
} else {
transformedGlyph2 = getTTGlyph(idx, glyph, rawInt, displayValue, factory);
}
} catch (final Exception e) {
//noinspection UnusedAssignment
transformedGlyph2 = null;
LogWriter.writeLog("Exception: " + e.getMessage());
}
//save so we can reuse if it occurs again in this TJ command
setEmbeddedCachedShape(id, transformedGlyph2);
}
return transformedGlyph2;
}
/*
* creates glyph from truetype font commands
*/
private PdfGlyph getTTGlyph(int idx, final String glyph, final int rawInt, final String displayValue, final GlyphFactory factory) {
if (isCorrupted) {
idx = rawInt;
}
PdfGlyph currentGlyph = null;
try {
if (idx != -1) {
//move the pointer to the commands
final int p = currentGlyf.getCharString(idx);
if (p != -1) {
if (factory.useFX()) {
currentGlyph = factory.getGlyph(currentGlyf, fontTable, currentHmtx, idx, (unitsPerEm / 1000f), vm, baseFontName);
} else if (TTGlyph.useHinting) {
currentGlyph = new TTGlyph(currentGlyf, fontTable, currentHmtx, idx, (unitsPerEm / 1000f), vm);
if (currentGlyph.failedOnHinting()) {
//some issue so use non-hinted version
currentGlyph = new TTGlyph(currentGlyf, fontTable, currentHmtx, idx, (unitsPerEm / 1000f), baseFontName);
}
} else {
currentGlyph = new TTGlyph(currentGlyf, fontTable, currentHmtx, idx, (unitsPerEm / 1000f), baseFontName);
}
} else if (!factory.useFX() && (" ".equals(glyph) || " ".equals(displayValue))) {
//Add a marker glyph in to record the number of the space glyph
currentGlyph = new MarkerGlyph(0, 0, 0, 0, baseFontName);
currentGlyph.setGlyphNumber(idx + 1);
}
}
} catch (final Exception ee) {
LogWriter.writeLog("Exception " + ee);
}
//if(glyph.equals("fl"))
return currentGlyph;
}
@Override
public void setEncodingToUse(final boolean hasEncoding, final int fontEncoding, final boolean isCIDFont) {
if (currentCMAP != null) {
if (isCorrupted) {
currentCMAP.setEncodingToUse(hasEncoding, fontEncoding, isCIDFont);
} else {
currentCMAP.setEncodingToUse(hasEncoding, fontEncoding, isCIDFont);
}
}
}
@Override
public int getConvertedGlyph(final int idx) {
if (currentCMAP == null) {
return idx;
} else {
return currentCMAP.convertIndexToCharacterCode(null, idx);
}
}
/**
* Return charstrings and subrs - used by PS to OTF converter
*
* @return
*/
@Override
public Map getCharStrings() {
if (currentCMAP != null) {
return currentCMAP.buildCharStringTable();
} else {
return currentGlyf.buildCharStringTable();
}
}
/*
* creates glyph from truetype font commands
*/
@Override
public float getTTWidth(final String glyph, final int rawInt, final String displayValue, final boolean TTstreamisCID) {
//use CMAP if not CID
int idx = rawInt;
float width = 0;
try {
if ((!TTstreamisCID)) {
idx = currentCMAP.convertIndexToCharacterCode(glyph, rawInt);
}
//if no value use post to lookup
if (idx < 1) {
idx = currentPost.convertGlyphToCharacterCode(glyph);
}
//if(idx!=-1)
width = currentHmtx.getWidth(idx);
} catch (final Exception e) {
LogWriter.writeLog("Attempting to read width " + e);
}
return width;
}
/*
* creates glyph from truetype font commands
*/
private float getUnscaledWidth(final String glyph, final int rawInt, final boolean TTstreamisCID) {
//use CMAP if not CID
int idx = rawInt;
float width;
try {
if ((!TTstreamisCID)) {
idx = currentCMAP.convertIndexToCharacterCode(glyph, rawInt);
}
//if no value use post to lookup
if (idx < 1) {
idx = currentPost.convertGlyphToCharacterCode(glyph);
}
//if(idx!=-1)
width = currentHmtx.getUnscaledWidth(idx);
} catch (final Exception e) {
width = 1000;
LogWriter.writeLog("Attempting to read width " + e);
}
return width;
}
@Override
public boolean isValidGIDtoCID(final int value) {
return CIDToGIDMap != null && CIDToGIDMap.length > value && CIDToGIDMap[value] > 0;
}
@Override
public void setGIDtoCID(final int[] cidToGIDMap) {
hasGIDtoCID = true;
this.CIDToGIDMap = cidToGIDMap;
}
/**
* return name of font or all fonts if TTC
* NAME will be LOWERCASE to avoid issues of capitalisation
* when used for lookup - if no name, will default to null
*
* Mode is PdfDecoder.SUBSTITUTE_* CONSTANT. RuntimeException will be thrown on invalid value
*/
public static String[] readFontNames(final FontData fontData, final int mode) {
/* setup read the table locations*/
final FontFile2 currentFontFile = new FontFile2(fontData);
//get type
//int fontType=currentFontFile.getType();
final int fontCount = currentFontFile.getFontCount();
final String[] fontNames = new String[fontCount];
/* read tables for names*/
for (int i = 0; i < fontCount; i++) {
currentFontFile.setSelectedFontIndex(i);
final Name currentName = new Name(currentFontFile);
final String name;
switch (mode) {
case PdfDecoderInt.SUBSTITUTE_FONT_USING_POSTSCRIPT_NAME:
name = currentName.getString(Name.POSTSCRIPT_NAME);
break;
case PdfDecoderInt.SUBSTITUTE_FONT_USING_FAMILY_NAME:
name = currentName.getString(Name.FONT_FAMILY_NAME);
break;
case PdfDecoderInt.SUBSTITUTE_FONT_USING_FULL_FONT_NAME:
name = currentName.getString(Name.FULL_FONT_NAME);
break;
default:
//tell user if invalid
throw new RuntimeException("Unsupported mode " + mode + ". Unable to resolve font names");
}
if (name == null) {
fontNames[i] = null;
} else {
fontNames[i] = name.toLowerCase();
}
}
if (fontData != null) {
fontData.close();
}
return fontNames;
}
/**
* Add font details to Map so we can access later
*/
public static void addStringValues(final FontData fontData, final Map fontDetails) {
/* setup read the table locations*/
final FontFile2 currentFontFile = new FontFile2(fontData);
//get type
//int fontType=currentFontFile.getType();
final int fontCount = currentFontFile.getFontCount();
/* read tables for names*/
for (int i = 0; i < fontCount; i++) {
currentFontFile.setSelectedFontIndex(i);
final Name currentName = new Name(currentFontFile);
final Map stringValues = currentName.getStrings();
if (stringValues != null) {
for (final Integer o : stringValues.keySet()) {
final Integer currentKey = o;
final int keyInt = currentKey;
if (keyInt < Name.stringNames.length) {
fontDetails.put(Name.stringNames[currentKey], stringValues.get(currentKey));
}
}
}
}
if (fontData != null) {
fontData.close();
}
}
@Override
public int readEmbeddedFont(final boolean TTstreamisCID, final byte[] fontDataAsArray, final FontData fontData) {
final FontFile2 currentFontFile;
isCID = TTstreamisCID;
/* setup read the table locations*/
if (fontDataAsArray != null) {
currentFontFile = new FontFile2(fontDataAsArray);
} else {
currentFontFile = new FontFile2(fontData);
}
//select font if TTC
//does nothing if TT
if (FontMappings.fontSubstitutionFontID == null) {
currentFontFile.setPointer(0);
} else {
final Integer fontID = FontMappings.fontSubstitutionFontID.get(fontName.toLowerCase());
if (fontID != null) {
currentFontFile.setPointer(fontID);
} else {
currentFontFile.setPointer(0);
}
}
currentHead = new Head(currentFontFile);
currentPost = new Post(currentFontFile);
//currentName=new Name(currentFontFile);
final Maxp currentMaxp = new Maxp(currentFontFile);
glyphCount = currentMaxp.getGlyphCount();
currentLoca = new Loca(currentFontFile, glyphCount, currentHead.getIndexToLocFormat());
isCorrupted = currentLoca.isCorrupted();
currentGlyf = new Glyf(currentFontFile, glyphCount, currentLoca.getIndices());
currentCFF = new CFF(currentFontFile, isCID, remappedCFFFont);
hasCFF = currentCFF.hasCFFData();
if (hasCFF) {
type = StandardFonts.OPENTYPE;
}
//currentCvt=new Cvt(currentFontFile);
if (TTGlyph.useHinting) {
//Classes in hinting package which we will delete in lgpl
vm = new TTVM(currentFontFile, currentMaxp);
}
currentHhea = new Hhea(currentFontFile);
FontBBox = currentHead.getFontBBox();
currentHmtx = new Hmtx(currentFontFile, glyphCount, currentHhea.getNumberOfHMetrics(), (int) FontBBox[3]);
//not all files have CMAPs
//if(!TTstreamisCID){
final int startPointer = currentFontFile.selectTable(FontFile2.CMAP);
if (startPointer != 0) {
currentCMAP = new CMAP(currentFontFile, startPointer);
}
//}
unitsPerEm = currentHead.getUnitsPerEm();
fontTable = new FontFile2(currentGlyf.getTableData(), true);
if (fontData != null) {
fontData.close();
}
return type;
}
@Override
public float[] getFontBoundingBox() {
return FontBBox;
}
@Override
public int getType() {
return type;
}
//flag if Loca broken so we need to try and Substitute
@Override
public boolean isCorrupted() {
return isCorrupted;
}
@Override
public void setCorrupted(final boolean corrupt) {
isCorrupted = corrupt;
}
@Override
public Table getTable(final int type) {
final Table table;
switch (type) {
case FontFile2.LOCA:
table = currentLoca;
break;
case FontFile2.CMAP:
table = currentCMAP;
break;
case FontFile2.HHEA:
table = currentHhea;
break;
case FontFile2.HMTX:
table = currentHmtx;
break;
case FontFile2.HEAD:
table = currentHead;
break;
default:
throw new RuntimeException("table not yet added to getTable)");
}
return table;
}
}