org.jpedal.fonts.Type1 Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of OpenViewerFX Show documentation
Show all versions of OpenViewerFX Show documentation
An Open Source JavaFX PDF Viewer
/*
* ===========================================
* 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-2015 IDRsolutions and Contributors.
*
* This file is part of JPedal/JPDF2HTML5
*
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* ---------------
* Type1.java
* ---------------
*/
package org.jpedal.fonts;
import java.io.BufferedReader;
import org.jpedal.utils.repositories.FastByteArrayOutputStream;
import java.io.StringReader;
import java.util.StringTokenizer;
import org.jpedal.utils.LogWriter;
/**
* handles type1 specifics
*/
public class Type1 extends PdfFont {
protected boolean isCID;
/** constant used in eexec and charset decode */
private static final int c1 = 52845;
/** constant used in eexec and charset decode */
private static final int c2 = 22719;
//number of random bytes in stream to ignore
private int skipBytes=4;
//Private DICT values
protected int[] blueValues;
protected int[] otherBlues;
protected int[] familyBlues;
protected int[] familyOtherBlues;
protected Double blueScale;
protected Integer blueShift;
protected Integer blueFuzz;
protected Double stdHW;
protected Double stdVW;
protected int[] stemSnapH;
protected int[] stemSnapV;
protected Boolean forceBold;
protected Integer languageGroup;
/**lookup for 1 byte draw commands*/
public static final String T1CcharCodes1Byte[]={"-Reserved-","hstem","-Reserved-","vstem",
"vmoveto","rlineto","hlineto","vlineto",
"rrcurveto","closePathT1","callsubr","return",
"escape","hsbwT1","endchar","-Reserved-",
"blend","-Reserved-","hstemhm","hintmask",
"cntrmask","rmoveto","hmoveto","vstemhm",
"rcurveline","rlinecurve","vvcurveto","hhcurveto",
"intint","callgsubr","vhcurveto","hvcurveto"};
/**lookup for 2 byte draw commands*/
public static final String T1C[]={"dotSection","vstem3","hstem3","and",
"or","not","seacT1","swbT1",
"store","abs","add","sub",
"div","load","neg","eq",
"callothersubT1","pop","drop","-Reserved-",
"put","get","ifelse","random",
"mul","-Reserved-","sqrt","dup",
"exch","index","roll","-Reserved-",
"-Reserved-","setcurrentpointT1","hflex","flex",
"hflex1","flex1"};
//flag if we store reverse map for index to CMAP (use dbt OTF conversion)
boolean trackIndices;
public static final char[] DEF_CHARS = "def".toCharArray();
/*
* creates glyph from truetype font commands
*
//protected T1Glyph getType1Glyph(T1Glyph current_path,int rawInt, String displayValue, float currentWidth) {
protected T1Glyph getType1Glyph(double x,double y,T1Glyph current_path,String glyph,int rawInt, String displayValue, float currentWidth,boolean isRecursive) {
boolean test=false;
byte[] glyphStream=(byte[]) charStrings.get(glyph);
System.out.println(glyph+" "+glyphStream.length+" "+charStrings);
for(int i=0;i31 | (nextVal==28)) { //number,SID or array
//vector used so several values can be returned
Vector v = getInt(glyphStream, p);
op[opCount] = ((Double) v.elementAt(0)).doubleValue();
p = ((Integer) v.elementAt(1)).intValue();
opCount++;
}else{ // operator
lastKey=key;
key = nextVal;
p++;
currentOp=0;
if(test){
for(int j=0;jMath.abs(dy));
for(int points=0;points<6;points=points+2){//first curve
x += op[points];
y += op[points+1];
pt[points]=(float) x;
pt[points+1]=(float) y;
}
current_path.curveTo(pt[0],pt[1],pt[2],pt[3],pt[4],pt[5]);
for(int points=0;points<4;points=points+2){//second curve
x += op[points+6];
y += op[points+7];
pt[points]=(float) x;
pt[points+1]=(float) y;
}
// last point
if ( isHorizontal ){
x += op[10];
y = y1;
}else{
x = x1;
y += op[10];
}
pt[4]=(float) x;
pt[5]=(float) y;
current_path.curveTo(pt[0],pt[1],pt[2],pt[3],pt[4],pt[5]);
}else if(test){
System.out.println(p+" "+key+" "+charCodes2Byte[key]+" <2<<"+op);
}
} else {
if(test)
System.out.println(charCodes1Byte[key]+" "+key);
if(key==0){ //reserved
}else if((key==1)|(key==3)|(key==18)|(key==23)){ //hstem vstem hstemhm vstemhm
hintCount+=opCount/2;
}else if(key==4){ //vmoveto
if((isFirst)&&(opCount==2))
currentOp++;
y=y+op[currentOp];
current_path.moveTo((float)x,(float)y);
}else if((key==5)){//rlineto
int lineCount=opCount/2;
while ( lineCount > 0 ){
x += op[currentOp];
y += op[currentOp+1];
current_path.lineTo((float)x,(float)y);
currentOp += 2;
lineCount--;
}
}else if((key==6)|(key==7)){//hlineto or vlineto
boolean isHor = ( key==6 );
int start=0;
while (start 0 ){
float[] coords=new float[6];
x += op[currentOp];
y += op[currentOp+1];
coords[0]=(float) x;
coords[1]=(float) y;
x += op[currentOp+2];
y += op[currentOp+3];
coords[2]=(float) x;
coords[3]=(float) y;
x += op[currentOp+4];
y += op[currentOp+5];
coords[4]=(float) x;
coords[5]=(float) y;
current_path.curveTo(coords[0],coords[1],coords[2],coords[3],coords[4],coords[5]);
currentOp += 6;
curveCount--;
}
}else if(key==10){ //callsubr
System.out.println("callsubr");
}else if(key==11){ //return
}else if(key==14){ //endchar
if(opCount==5){ //allow for width and 4 chars
opCount--;
currentOp++;
}
if(opCount==4){
StandardFonts.checkLoaded(StandardFonts.STD);
float adx=(float)(x+op[currentOp]);
float ady=(float)(y+op[currentOp+1]);
String bchar=StandardFonts.getUnicodeChar(StandardFonts.STD ,(int)op[currentOp+2]);
String achar=StandardFonts.getUnicodeChar(StandardFonts.STD ,(int)op[currentOp+3]);
current_path=getType1CGlyph(0,0,current_path,bchar,rawInt, "", 0,true);
current_path.closePath();
current_path.moveTo(adx,ady);
current_path=(getType1CGlyph(adx,ady,current_path,achar,rawInt, "", 0,true));
}else
current_path.closePath();
p =dicEnd;
}else if(key==16){ //blend
}else if((key==19)|(key==20)){ //hintmask //cntrmask
if((lastKey==18)|(lastKey==1))
hintCount+=opCount/2;
int count=hintCount;
while(count>0){
p++;
count=count-8;
}
}else if(key==21){//rmoveto
if((isFirst)&&(opCount==3))
currentOp++;
x=x+op[currentOp];
y=y+op[currentOp+1];
current_path.moveTo((float)x,(float)y);
}else if(key==22){ //hmoveto
if((isFirst)&&(opCount==2))
currentOp++;
x=x+op[currentOp];
current_path.moveTo((float)x,(float)y);
}else if(key==24){ //rcurveline
//curves
int curveCount=( opCount - 2 ) / 6;
while ( curveCount > 0 ){
float[] coords=new float[6];
x += op[currentOp];
y += op[currentOp+1];
coords[0]=(float) x;
coords[1]=(float) y;
x += op[currentOp+2];
y += op[currentOp+3];
coords[2]=(float) x;
coords[3]=(float) y;
x += op[currentOp+4];
y += op[currentOp+5];
coords[4]=(float) x;
coords[5]=(float) y;
current_path.curveTo(coords[0],coords[1],coords[2],coords[3],coords[4],coords[5]);
currentOp += 6;
curveCount--;
}
// line
x += op[currentOp];
y += op[currentOp+1];
current_path.lineTo((float)x,(float)y);
currentOp += 2;
}else if(key==25){ //rlinecurve
//lines
int lineCount=( opCount - 6 ) / 2;
while ( lineCount > 0 ){
x += op[currentOp];
y += op[currentOp+1];
current_path.lineTo((float)x,(float)y);
currentOp += 2;
lineCount--;
}
//curves
float[] coords=new float[6];
x += op[currentOp];
y += op[currentOp+1];
coords[0]=(float) x;
coords[1]=(float) y;
x += op[currentOp+2];
y += op[currentOp+3];
coords[2]=(float) x;
coords[3]=(float) y;
x += op[currentOp+4];
y += op[currentOp+5];
coords[4]=(float) x;
coords[5]=(float) y;
current_path.curveTo(coords[0],coords[1],coords[2],coords[3],coords[4],coords[5]);
currentOp += 6;
}else if((key==26)|(key==27)){ //vvcurve hhcurveto
boolean isVV=(key==26);
if ( (opCount & 1) ==1 ){
if(isVV)
x += op[0];
else
y += op[0];
currentOp++;
}
//note odd co-ord order
while (currentOp= 4 ){
opCount -= 4;
if ( isHor )
x += op[currentOp];
else
y += op[currentOp];
pt[0]=(float) x;
pt[1]=(float) y;
x += op[currentOp+1];
y += op[currentOp+2];
pt[2]=(float) x;
pt[3]=(float) y;
if ( isHor ){
y += op[currentOp+3];
if ( opCount ==1 )
x += op[currentOp+4];
}else{
x += op[currentOp+3];
if ( opCount == 1 )
y += op[currentOp+4];
}
pt[4]=(float) x;
pt[5]=(float) y;
current_path.curveTo(pt[0],pt[1],pt[2],pt[3],pt[4],pt[5]);
currentOp += 4;
isHor = !isHor;
}
}else{
System.out.println(p+">>>>>"+hintCount+">>>>>>"+key+" "+charCodes1Byte[key]+" <1<<"+op);
for(int j=0;j0) {
isFontEmbedded = true;
}
glyphs.setFontEmbedded(true);
}
/**
* read the diff table from a type 1 font
*/
private void readDiffEncoding(final BufferedReader br) throws Exception {
String line, name, rawVal,base,val;
int code,ptr;
while ((line = br.readLine()) != null) {
line = line.trim();
//exit at end
if (line.startsWith("readonly")) {
break;
}
//read each mapping
if (line.startsWith("dup") && line.contains("/")) {
final StringTokenizer info = new StringTokenizer(line, " /");
if (info.countTokens() >= 3) {
info.nextToken(); // discard dup
rawVal=info.nextToken();
ptr=rawVal.indexOf('#');
if(ptr==-1) {
code = Integer.parseInt(rawVal); //code
} else{
base=rawVal.substring(0,ptr);
val=rawVal.substring(ptr+1,rawVal.length());
code =Integer.parseInt(val,Integer.parseInt(base));
}
name = info.nextToken(); //name
putChar(code, name);
final char c=name.charAt(0);
if (c=='B' || c=='C' || c=='c' || c=='G') {
int i = 1;
final int l=name.length();
while (!isHex && i = '0' && c <= '9')|| (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) {
//is okay
} else {
isAscii = false;
break;
}
}
if (charstringStart != -1) {
final FastByteArrayOutputStream bos = new FastByteArrayOutputStream(end-charstringStart);
//allow for offset in real pfb file
if (isFontSubstituted && !isAscii) {
charstringStart = charstringStart + 2 + skipBytes;
}
for (i = charstringStart; i < end; i++) {
if (!isAscii){
cipher = cont[i] & 0xff;
}else {
int chars = 0;
tmp = new StringBuilder();
//get 2 chars for next hex value ignoring
// whitespace/returns,etc
while (chars < 2) {
cipher = cont[i] & 0xff;
i++;
if (cipher != 10 && cipher != 13 && cipher != 9 && cipher != 32) {
tmp.append((char)cipher);
chars++;
}
}
i--;
//convert to hex value
cipher = Integer.parseInt(tmp.toString(), 16);
}
plain = (cipher ^ (r >> 8));
r = ((cipher + r) * c1 + c2) & 0xffff;
if (i > charstringStart + n) {
bos.write(plain);
}
}
cont = bos.toByteArray();
}
/** now charset decode and store */
//n=4;//set default value for n
//read values from the stream
final BufferedReader br =new BufferedReader(new StringReader(new String(cont)));
while (true) {
line = br.readLine();
if (line == null) {
break;
}
//if(line.startsWith("/two"))
//System.out.println(line);
//if(!line.startsWith("/"))
//get new value for n second value
if(line.startsWith("/lenIV")){
final StringTokenizer vals=new StringTokenizer(line);
vals.nextToken(); //drop first value
skipBytes=Integer.parseInt(vals.nextToken());
//System.out.println(line);
}
}
br.close();
/**extract the contents*/
//find dictionary entries
final int l=cont.length;
int p=0;
charstringStart=-1;
int subrsStart=-1;
int blueValuesStart = -1;
int otherBluesStart = -1;
int familyBluesStart = -1;
int familyOtherBluesStart = -1;
int blueScaleStart = -1;
int blueShiftStart = -1;
int blueFuzzStart = -1;
int stdHWStart = -1;
int stdVWStart = -1;
int stemSnapHStart = -1;
int stemSnapVStart = -1;
int forceBoldStart = -1;
int languageGroupStart = -1;
final char[] charstringsChars = "/CharStrings".toCharArray();
final char[] subrsChars = "/Subrs".toCharArray();
final char[] blueValuesChars = "/BlueValues".toCharArray();
final char[] otherBluesChars = "/OtherBlues".toCharArray();
final char[] familyBluesChars = "/FamilyBlues".toCharArray();
final char[] familyOtherBluesChars = "/FamilyOtherBlues".toCharArray();
final char[] blueScaleChars = "/BlueScale".toCharArray();
final char[] blueShiftChars = "/BlueShift".toCharArray();
final char[] blueFuzzChars = "/BlueFuzz".toCharArray();
final char[] stdHWChars = "/StdHW".toCharArray();
final char[] stdVWChars = "/StdVW".toCharArray();
final char[] stemSnapHChars = "/StemSnapH".toCharArray();
final char[] stemSnapVChars = "/StemSnapV".toCharArray();
final char[] forceBoldChars = "/ForceBold".toCharArray();
final char[] languageGroupChars = "/LanguageGroup".toCharArray();
while(p-1 && charstringStart>-1 &&
blueValuesStart>-1 && otherBluesStart>-1 &&
familyBluesStart>-1 && familyOtherBluesStart>-1 &&
blueScaleStart>-1 && blueShiftStart>-1 && blueFuzzStart>-1 &&
stdHWStart>-1 && stdVWStart>-1 &&
stemSnapHStart>-1 && stemSnapVStart>-1 &&
forceBoldStart>-1 && languageGroupStart>-1) {
break;
}
p++;
}
/**extract charstrings*/
if(charstringStart==-1){
this.isFontSubstituted=false;
LogWriter.writeLog("No glyph data found");
}else {
glyphCount=extractFontData(skipBytes,cont,charstringStart,rd,l,nd);
}
/**extract subroutines*/
if(subrsStart>-1) {
extractSubroutineData(skipBytes,cont,subrsStart,charstringStart,rd,l,nd);
}
if(blueValuesStart>-1) {
blueValues = readIntArray(cont, blueValuesStart);
}
if (otherBluesStart>-1) {
otherBlues = readIntArray(cont, otherBluesStart);
}
if (familyBluesStart>-1) {
familyBlues = readIntArray(cont, familyBluesStart);
}
if (familyOtherBluesStart>-1) {
familyOtherBlues = readIntArray(cont, familyOtherBluesStart);
}
if (stdHWStart>-1) {
stdHW = readReal(cont, stdHWStart);
}
if (stdVWStart>-1) {
stdVW = readReal(cont, stdVWStart);
}
if (stemSnapHStart>-1) {
stemSnapH = readIntArray(cont, stemSnapHStart);
}
if (stemSnapVStart>-1) {
stemSnapV = readIntArray(cont, stemSnapVStart);
}
if (blueScaleStart>-1) {
blueScale = readReal(cont, blueScaleStart);
}
if (blueShiftStart>-1) {
blueShift = readInteger(cont, blueShiftStart);
}
if (blueFuzzStart>-1) {
blueFuzz = readInteger(cont, blueFuzzStart);
}
if (forceBoldStart>-1) {
int j = forceBoldStart;
int forceBoldEnd = -1;
while (j < l && forceBoldEnd == -1) {
if (checkForString(cont, j, DEF_CHARS)) {
forceBoldEnd = j;
}
j++;
}
final String val = new String(cont, forceBoldStart, forceBoldEnd-forceBoldStart);
try {
forceBold = Boolean.parseBoolean(val);
} catch(final NumberFormatException e) {
LogWriter.writeLog("Exception " + e);
}
}
if (languageGroupStart>-1) {
languageGroup = readInteger(cont, languageGroupStart);
}
return glyphCount;
}
/**
* Reads an integer
*/
static Integer readInteger(final byte[] data, final int offset) {
final int l = data.length;
int j = offset;
int end = -1;
while (j < l && end == -1) {
if (checkForString(data, j, DEF_CHARS)) {
end = j;
}
j++;
}
final String val = new String(data, offset, end-offset);
try {
return Integer.parseInt(val);
} catch(final NumberFormatException e) {
LogWriter.writeLog("Exception in handling Integer in Type1 "+e);
return null;
}
}
/**
* Reads a real or array containing a single real
*/
static Double readReal(final byte[] data, final int offset) {
final int l = data.length;
int j = offset;
int end = -1;
while (j < l && end == -1) {
if (checkForString(data, j, DEF_CHARS) || data[j] == ']' || data[j] == '\n') {
end = j;
}
j++;
}
String val = new String(data, offset, end-offset);
if (val.contains("[")) {
final String[] stringParts = val.split("\\[");
if (stringParts.length < 2) {
return null;
}
val = stringParts[1];
}
try {
return Double.parseDouble(val);
} catch(final NumberFormatException e) {
LogWriter.writeLog("Exception in handling real in Type1 "+e);
return null;
}
}
/**
* Reads an int array in the format [1 2 3]. Returns null if it can't be read, and stores -1 in the place of any
* values which can't be read.
* @param data The data to read from
* @param start The start of the array
* @return An int[] representation of the array
*/
private static int[] readIntArray(final byte[] data, final int start) {
int[] result = null;
final int l = data.length;
int j= start;
int end = -1;
while (j < l && end == -1) {
if (data[j] == ']' || data[j] == '/' || data[j] == '\n') {
end = j;
}
j++;
}
if (end != -1) {
String values = new String(data, start, end-start);
if (values.contains("[")) {
final String[] stringParts = values.split("\\[");
if (stringParts.length < 2) {
return null;
}
values = stringParts[1];
}
values = values.trim();
final String[] stringValues = values.split(" ");
result = new int[stringValues.length];
for (j=0; j< stringValues.length; j++) {
try {
result[j] = Integer.parseInt(stringValues[j].split("\\.")[0]);
} catch(final NumberFormatException e) {
LogWriter.writeLog("Exception in handling IntArray "+e);
result[j] = -1;
}
}
}
return result;
}
/**
* Checks for a sequence of chars at a given offset in a byte array.
*/
private static boolean checkForString(final byte[] data, final int offset, final char[] chars) {
for (int i=0; i\n");
// for(int aa=start;aa> 8));
//here�
r = ((cipher + r) * c1 + c2) & 0xffff;
if(i>=skipBytes) {
bos.write(plain);
// System.out.println(plain);
//else
// System.out.println("SKIP="+plain);
}
}
return bos.toByteArray();
}
}