All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jpedal.fonts.Type1 Maven / Gradle / Ivy

The newest version!
/*
 * ===========================================
 * Java Pdf Extraction Decoding Access Library
 * ===========================================
 *
 * Project Info:  http://www.idrsolutions.com
 * Help section for developers at http://www.idrsolutions.com/java-pdf-library-support/
 *
 * (C) Copyright 1997-2013, IDRsolutions and Contributors.
 *
 * 	This file is part of JPedal
 *
     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 java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.util.StringTokenizer;

import org.jpedal.io.PdfObjectReader;
import org.jpedal.utils.LogWriter;

/**
 * handles type1 specifics
 */
public class Type1 extends PdfFont {

	private static final long serialVersionUID = 3770968246210164415L;

	protected boolean isCID = false;

	/** constant used in eexec and charset decode */
	private final static int c1 = 52845;

	/** constant used in eexec and charset decode */
	private final static int c2 = 22719;

	// number of random bytes in stream to ignore
	private int skipBytes = 4;

	/** lookup for 1 byte draw commands */
	public final static 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 final static 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 = false;

	/*
	 * 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;j 0) this.isFontEmbedded = true;

		this.glyphs.setFontEmbedded(true);
	}

	/**
	 * read the diff table from a type 1 font
	 */
	private void readDiffEncoding(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("/")) {
				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);

					char c = name.charAt(0);
					if (c == 'B' || c == 'C' || c == 'c' || c == 'G') {
						int i = 1, l = name.length();
						while (!this.isHex && i < l)
							this.isHex = Character.isLetter(name.charAt(i++));
					}
				}
			}
		}
	}

	/** store embedded differences */
	protected final void putChar(int charInt, String mappedChar) {

		if (this.diffs == null) this.diffs = new String[this.maxCharCount];

		this.diffs[charInt] = mappedChar;

		if (!this.hasEncoding && !this.isCID) {

			if (StandardFonts.getUnicodeName(mappedChar) != null) {
				putMappedChar(charInt, mappedChar);
			}
			else { // needed to convert back for HTML5
				this.nonStandardMappings.put(mappedChar, charInt);
			}

		}
	}

	/**
	 * read the encoded part from a type 1 font
	 */
	private int readEncodedContent(byte[] cont) throws Exception {

		int glyphCount = 0;
		String line;
		String rd = "rd", nd = "nd";
		int size = cont.length, start = -1, end = -1, i, cipher, plain;
		StringBuilder tmp;

		for (i = 4; i < size; i++) { // find the start of /CharStrings (which is exec
			if ((cont[i - 3] == 101) && (cont[i - 2] == 120) && (cont[i - 1] == 101) && (cont[i] == 99)) {
				start = i + 1;
				while (cont[start] == 10 || cont[start] == 13)
					start++;
				i = size;
			}
		}

		if (start != -1) { // find the end
			for (i = start; i < size - 10; i++) {
				if ((cont[i] == 99) && (cont[i + 1] == 108) && (cont[i + 2] == 101) && (cont[i + 3] == 97) && (cont[i + 4] == 114)
						&& (cont[i + 5] == 116) && (cont[i + 6] == 111) && (cont[i + 7] == 109) && (cont[i + 8] == 97) && (cont[i + 9] == 114)
						&& (cont[i + 10] == 107)) {
					end = i - 1;
					while ((cont[end] == 10) || (cont[end] == 13))
						end--;
					i = size;
				}
			}
		}

		if (end == -1) end = size;

		/** now decode the array */
		int r = 55665;
		int n = 4;

		/** workout if binary or ascii - assume true and disprove */
		boolean isAscii = true;
		for (i = start; i < start + (n * 2); i++) {
			char c = (char) (cont[i]);
			if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) {
				// is okay
			}
			else {
				isAscii = false;
				break;
			}
		}

		if (start != -1) {

			ByteArrayOutputStream bos = new ByteArrayOutputStream(end - start);

			// allow for offset in real pfb file
			if (this.isFontSubstituted && !isAscii) start = start + 2 + this.skipBytes;

			for (i = start; 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 > start + n) bos.write(plain);

			}

			bos.close();
			cont = bos.toByteArray();
		}

		/** now charset decode and store */
		// n=4;//set default value for n

		// read values from the stream
		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")) {
				StringTokenizer vals = new StringTokenizer(line);
				vals.nextToken(); // drop first value
				this.skipBytes = Integer.parseInt(vals.nextToken());
				// System.out.println(line);
			}
			/**
			 * else if(line.indexOf("pop}")!=-1){ //System.out.println("RD="+line); StringTokenizer vals=new StringTokenizer(line,"{ ");
			 * rd=vals.nextToken().substring(1); }else if(line.indexOf("def}")!=-1){ //System.out.println("ND="+line); StringTokenizer vals=new
			 * StringTokenizer(line,"{ "); nd=vals.nextToken().substring(1); }else if(line.indexOf("put}")!=-1){ //System.out.println("NP="+line);
			 * StringTokenizer vals=new StringTokenizer(line,"{ "); np=vals.nextToken().substring(1); }
			 */
		}

		br.close();

		/** extract the contents */

		// find the start (/CharStrings) and /Subrs
		int l = cont.length;
		int p = 0;
		int subStart = -1;
		start = -1;

		while (p < l) {

			if (p == l) break;

			if ((p + 11 < l) && cont[p] == 47 && cont[p + 1] == 67 && cont[p + 2] == 104 && cont[p + 3] == 97 && cont[p + 4] == 114
					&& cont[p + 5] == 83 && cont[p + 6] == 116 && cont[p + 7] == 114 && cont[p + 8] == 105 && cont[p + 9] == 110
					&& cont[p + 10] == 103 && cont[p + 11] == 115) start = p + 11;
			else
				if ((p + 5 < l) && cont[p] == 47 && cont[p + 1] == 83 && cont[p + 2] == 117 && cont[p + 3] == 98 && cont[p + 4] == 114
						&& cont[p + 5] == 115) subStart = p + 6;

			if (subStart > -1 && start > -1) break;

			p++;

		}

		/** extract charstrings */
		if (start == -1) {
			this.isFontSubstituted = false;

			if (LogWriter.isOutput()) LogWriter.writeLog("No glyph data found");
		}
		else glyphCount = extractFontData(this.skipBytes, cont, start, rd, l, nd);

		/** extract subroutines */
		if (subStart > -1) extractSubroutineData(this.skipBytes, cont, subStart, start, rd, l, nd);

		return glyphCount;
	}

	private void extractSubroutineData(int skipBytes, byte[] cont, int start, int charStart, String rd, int l, String nd) throws IOException {

		int count;

		// move to start of first value
		while (cont[start] == 32 || cont[start] == 10 || cont[start] == 13)
			start++;

		// read the count
		StringBuilder tmp = new StringBuilder();
		while (true) {
			char c = (char) cont[start];
			if (c == ' ') break;
			tmp.append(c);
			start++;
		}

		count = Integer.parseInt(tmp.toString());

		for (int i = 0; i < count; i++) {

			// read the dup
			while ((start < l)) {
				if (((cont[start - 2] == 100) && (cont[start - 1] == 117) && (cont[start] == 112)) | (start == charStart)) break;
				start++;
			}

			if (start == charStart) {
				i = count;
			}
			else {
				// move to start of first value
				while ((cont[start + 1] == 32))
					start++;

				// read the count
				StringBuilder glyph = new StringBuilder("subrs");
				while (true) {
					start++;
					char c = (char) cont[start];
					if (c == ' ') break;

					glyph.append(c);

				}

				// read the byte count
				tmp = new StringBuilder();
				while (true) {
					start++;
					char c = (char) cont[start];
					if (c == ' ') break;
					tmp.append(c);

				}

				int byteCount = Integer.parseInt(tmp.toString());

				// skip any more spaces
				while ((cont[start] == 32))
					start++;

				// skip RD and blank space and random bytes
				start = start + rd.length() + 1;
				byte[] stream = getStream(skipBytes, start, byteCount, cont);

				// store table
				this.glyphs.setCharString(glyph.toString(), stream, i);

				start = start + byteCount + nd.length();

				// move to start of next value
				// while((start<=cont.length)&&(cont[start] != 47))
				// start++;
			}
		}
	}

	private int extractFontData(int skipBytes, byte[] cont, int start, String rd, int l, String nd) throws IOException {

		int total = cont.length, glyphCount = 0;

		// move to start of first value
		while (start < total && cont[start] != 47)
			start++;

		int end = start;

		// System.out.println("----->\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);

		}

		bos.close();

		return bos.toByteArray();
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy