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

org.jpedal.fonts.tt.conversion.PS2OTFFontWriter 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


 *
 * ---------------
 * PS2OTFFontWriter.java
 * ---------------
 */
package org.jpedal.fonts.tt.conversion;

import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;

import org.jpedal.fonts.PdfFont;
import org.jpedal.fonts.TrueType;
import org.jpedal.fonts.Type1C;
import org.jpedal.fonts.glyph.PdfGlyph;
import org.jpedal.fonts.glyph.PdfJavaGlyphs;
import org.jpedal.fonts.glyph.T1Glyphs;
import org.jpedal.fonts.tt.FontFile2;
import org.jpedal.fonts.tt.Hhea;
import org.jpedal.fonts.tt.Hmtx;
import org.jpedal.fonts.tt.TTGlyphs;
import org.jpedal.utils.LogWriter;

public class PS2OTFFontWriter extends FontWriter {

	private static final long serialVersionUID = 3332020682301464589L;

	byte[] cff;

	byte[] cmap = null;

	PdfFont pdfFont, originalFont;
	PdfJavaGlyphs glyphs;

	int minCharCode;
	int maxCharCode;

	// Glyph Metrics
	private int xAvgCharWidth = 0;
	private double xMaxExtent = Double.MIN_VALUE;
	private double minRightSideBearing = Double.MAX_VALUE;
	private double minLeftSideBearing = Double.MAX_VALUE;
	private double advanceWidthMax = Double.MIN_VALUE;
	private double lowestDescender = -1;
	private double highestAscender = 1;
	private float[] fontBBox = new float[4];
	private double emSquareSize = 1000;
	private int[] advanceWidths;
	private int[] lsbs = null;
	private HashMap widthMap;
	private String[] glyphList = null;

	FontFile2 orginTTTables = null;

	public PS2OTFFontWriter(PdfFont originalFont, byte[] rawFontData, String fileType, HashMap widths) throws Exception {

		boolean is1C = fileType.equals("cff");

		this.name = originalFont.getBaseFontName();

		this.widthMap = widths;

		// adjust for TT or Postscript
		String[] tablesUsed = new String[] { "CFF ",
				// "FFTM",
				// "GDEF",
				"OS/2", "cmap", "head", "hhea", "hmtx", "maxp", "name", "post" };

		if (fileType.equals("ttf")) {

			this.subType = TTF;

			// read the data into our T1/t1c object so we can then parse
			this.glyphs = originalFont.getGlyphData();
			this.pdfFont = new TrueType(rawFontData, this.glyphs);

			this.orginTTTables = new FontFile2(rawFontData);

			// order for writing out tables
			// (header if different order) so we also need to translate
			// tablesUsed=new String[]{"head","hhea","maxp",
			// "OS/2",
			// "hmtx",
			// "cmap",
			// "loca",
			// "glyf",
			// "name",
			// "post"
			// };

			// new woff table order
			tablesUsed = new String[] { "OS/2", "cmap", "cvt ", "fpgm", "glyf", "head", "hhea", "hmtx", "loca", "maxp", "name", "post", "prep"

			};

			for (int i = 0; i < tablesUsed.length; i++) {
				this.IDtoTable.put(tablesUsed[i], i);
			}

		}
		else {
			/**
			 * for the moment we reread with diff paramters to extract other data
			 */
			// read the data into our T1/t1c object so we can then parse
			this.glyphs = new T1Glyphs(false, is1C);

			this.pdfFont = new Type1C(rawFontData, this.glyphs, is1C);

		}

		this.glyphCount = this.glyphs.getGlyphCount();

		this.originalFont = originalFont;
		this.cff = rawFontData;

		this.tableList = new ArrayList();

		this.numTables = tablesUsed.length;

		int floor = 1;
		while (floor * 2 <= this.numTables)
			floor *= 2;

		this.searchRange = floor * 16;

		this.entrySelector = 0;
		while (Math.pow(2, this.entrySelector) < floor)
			this.entrySelector++;

		this.rangeShift = (this.numTables * 16) - this.searchRange;

		this.tableList.addAll(Arrays.asList(tablesUsed).subList(0, this.numTables));

		// location of tables
		this.checksums = new int[this.tableCount][1];
		this.tables = new int[this.tableCount][1];
		this.tableLength = new int[this.tableCount][1];

		this.type = OPENTYPE;

		// @sam - used to hack in font
		// if(name.contains("KTBBOD+HermesFB-Bold")){
		// // if(name.contains("ZapfEchos")){
		//
		// try{
		// File file=new File("C:/Users/Sam/Downloads/HermesFB-Regular.otf");
		// // File file=new File("C:/Users/Sam/Downloads/zapfechosWorks.otf");
		// int len=(int)file.length();
		// byte[] data=new byte[len];
		// FileInputStream fis=new FileInputStream(file);
		// fis.read(data);
		// fis.close();
		// FontFile2 f=new FontFile2(data,false);
		//
		//
		// for(int l=0;l this.advanceWidths[i] ? this.advanceWidthMax : this.advanceWidths[i];
				totalWidth += this.advanceWidths[i];
			}
		}

		// Store average width
		if (this.glyphCount > 0) this.xAvgCharWidth = (int) ((double) totalWidth / (double) this.glyphCount);

		// Collect glyph metrics
		double maxX = Double.MIN_VALUE;

		int iterationCount = this.glyphCount;
		if (this.originalFont.getCIDToGIDMap() != null) {
			if (this.originalFont.getCIDToGIDMap().length < iterationCount) {
				iterationCount = this.originalFont.getCIDToGIDMap().length;
			}
		}

		for (int i = 0; i < iterationCount && i < 256; i++) {
			PdfGlyph glyph = this.glyphs.getEmbeddedGlyph(new org.jpedal.fonts.glyph.T1GlyphFactory(), this.pdfFont.getMappedChar(i, false),
					new float[][] { { 1, 0 }, { 0, 1 } }, i, this.pdfFont.getGlyphValue(i), this.pdfFont.getWidth(i),
					this.pdfFont.getMappedChar(i, false));

			if (glyph != null && glyph.getShape() != null) {

				Rectangle2D area = glyph.getShape().getBounds2D();

				double lsb = area.getMinX();
				double rsb = this.advanceWidths[i] - area.getMaxX();
				double extent = area.getMinX() + area.getWidth();

				this.minLeftSideBearing = this.minLeftSideBearing < lsb ? this.minLeftSideBearing : lsb;
				this.minRightSideBearing = this.minRightSideBearing < rsb ? this.minRightSideBearing : rsb;
				this.xMaxExtent = this.xMaxExtent > extent ? this.xMaxExtent : extent;
				this.lowestDescender = this.lowestDescender < area.getMinY() ? this.lowestDescender : area.getMinY();
				this.highestAscender = this.highestAscender > area.getMaxY() ? this.highestAscender : area.getMaxY();
				maxX = maxX > area.getMaxX() ? maxX : area.getMaxX();
			}
		}

		if (this.originalFont.is1C()) {
			this.fontBBox = this.pdfFont.FontBBox;
		}
		else {
			this.fontBBox = this.originalFont.getFontBounds();
		}

		this.minLeftSideBearing = this.minLeftSideBearing < this.fontBBox[0] ? this.minLeftSideBearing : this.fontBBox[0];
		this.lowestDescender = this.lowestDescender < this.fontBBox[1] ? this.lowestDescender : this.fontBBox[1];
		maxX = maxX > this.fontBBox[2] ? maxX : this.fontBBox[2];
		this.highestAscender = this.highestAscender > this.fontBBox[3] ? this.highestAscender : this.fontBBox[3];

		this.fontBBox = new float[] { (float) this.minLeftSideBearing, (float) this.lowestDescender, (float) maxX, (float) this.highestAscender };
	}

	@Override
	public byte[] getTableBytes(int tableID) {

		byte[] fontData = new byte[0];

		FontTableWriter tableWriter = null;

		switch (tableID) {

			case CFF:
				if (this.pdfFont.is1C()) {
					// fix bad commands in CFF data
					fontData = (new CFFFixer(this.cff)).getBytes();
				}
				else {
					// convert type 1 to 1c
					tableWriter = new CFFWriter(this.glyphs, this.name);

					// Fetch glyph names and metrics
					CFFWriter cffWriter = (CFFWriter) tableWriter;
					this.glyphList = cffWriter.getGlyphList();
					this.advanceWidths = cffWriter.getWidths();
					this.lsbs = cffWriter.getBearings();
					this.fontBBox = cffWriter.getBBox();
					this.emSquareSize = cffWriter.getEmSquareSize();

					this.highestAscender = this.fontBBox[3];
					this.lowestDescender = this.fontBBox[1];
					this.advanceWidthMax = 0;

					// Calculate metrics
					int totalWidth = 0;
					for (int i = 0; i < this.advanceWidths.length; i++) {
						this.advanceWidthMax = this.advanceWidthMax > this.advanceWidths[i] ? this.advanceWidthMax : this.advanceWidths[i];
						totalWidth += this.advanceWidths[i];
						this.minLeftSideBearing = this.minLeftSideBearing < this.lsbs[i] ? this.minLeftSideBearing : this.lsbs[i];
					}

					if (this.glyphCount > 0) this.xAvgCharWidth = (int) ((double) totalWidth / (double) this.glyphCount);

					// try {
					// ByteArrayOutputStream bos = new ByteArrayOutputStream();
					// BufferedReader reader = new BufferedReader(new FileReader(new File("C:\\Users\\Sam\\Desktop\\FontForgeAmerican.txt")));
					//
					// while (reader.ready()) {
					// String line = reader.readLine();
					// String first = line.split("\t")[0];
					// first = first.replaceAll("[^01]", "");
					// if (first.length() == 8) {
					// int mag = 128;
					// byte val = 0;
					// for (int i=0; i<8; i++) {
					// boolean is1 = first.charAt(i) == '1';
					// if (is1)
					// val += mag;
					//
					// mag = mag / 2;
					// }
					//
					// bos.write(val);
					// fontData = bos.toByteArray();
					// }
					// }
					// }
				}
				break;

			case HEAD:
				if (this.subType == TTF) {
					fontData = this.orginTTTables.getTableBytes(HEAD);
				}
				else {
					tableWriter = new HeadWriter(this.fontBBox);
				}

				break;

			case CMAP:
				if (this.subType == TTF) {
					// byte [] data = orginTTTables.getTableBytes(CMAP);
					// if(data.length>0){
					tableWriter = new CMAPWriter(this.name, this.pdfFont, this.originalFont, this.glyphs, this.glyphList);
					// }
					// //identify files which does not contain cmap , cidToGidCmap or ToUnicode data
					// else if(originalFont.getCIDToGIDStream()==null && originalFont.getToUnicode()==null){
					// tableWriter=new CMAPWriter(name,pdfFont,originalFont,glyphs,glyphList);
					// break;
					// }
					// else{
					// fontData=data;
					// }
				}
				else {
					// if(name.contains("BKJFHK+HermesFB-Regular")){
					tableWriter = new CMAPWriter(this.name, this.pdfFont, this.originalFont, this.glyphs, this.glyphList);
					this.minCharCode = tableWriter.getIntValue(FontTableWriter.MIN_CHAR_CODE);
					this.maxCharCode = tableWriter.getIntValue(FontTableWriter.MAX_CHAR_CODE);
					// }
				}
				break;

			case GLYF:
				fontData = this.orginTTTables.getTableBytes(GLYF);
				break;

			case HHEA:
				if (this.subType == TTF) {
					TTGlyphs ttGlyphs = (TTGlyphs) this.glyphs;
					Hmtx hmtx = (Hmtx) ttGlyphs.getTable(HMTX);
					Hhea hhea = (Hhea) ttGlyphs.getTable(FontFile2.HHEA);

					boolean changed = false;

					// If advanceWidthMax is 0 recalculate
					int localAdvanceWidthMax = hhea.getIntValue(Hhea.ADVANCEWIDTHMAX);
					if (localAdvanceWidthMax == 0) {
						for (int z = 0; z < this.glyphs.getGlyphCount(); z++) {
							int temp = (int) hmtx.getUnscaledWidth(z);
							localAdvanceWidthMax = temp > localAdvanceWidthMax ? temp : localAdvanceWidthMax;
						}

						// Mark if changed so we can regenerate
						if (localAdvanceWidthMax != hhea.getIntValue(Hhea.ADVANCEWIDTHMAX)) {
							changed = true;
						}
					}

					if (changed) {
						// if advancewidthmax <= 0 calculate value from hmtx unscaled width [see reallybad.pdf case 13246]
						tableWriter = new HheaWriter(this.glyphs, hhea.getIntValue(Hhea.XMAXEXTENT), hhea.getIntValue(Hhea.MINIMUMRIGHTSIDEBEARING),
								hhea.getIntValue(Hhea.MINIMUMRIGHTSIDEBEARING), localAdvanceWidthMax, hhea.getIntValue(Hhea.DESCENDER),
								hhea.getIntValue(Hhea.ASCENDER));
					}
					else {
						fontData = this.orginTTTables.getTableBytes(HHEA);
					}
				}
				else {
					tableWriter = new HheaWriter(this.glyphs, this.xMaxExtent, this.minRightSideBearing, this.minLeftSideBearing,
							this.advanceWidthMax, this.lowestDescender, this.highestAscender);
				}
				break;

			case HMTX:
				if (this.subType == TTF) {
					fontData = this.orginTTTables.getTableBytes(HMTX);
				}
				else {
					tableWriter = new HmtxWriter(this.glyphs, this.advanceWidths, this.lsbs);
				}
				break;

			case LOCA:
				if (this.subType == TTF) {
					fontData = this.orginTTTables.getTableBytes(LOCA);
				}
				else {
					tableWriter = new LocaWriter(this.name, this.pdfFont, this.originalFont, this.glyphs, this.glyphList);
				}

				break;

			case OS2:
				if (this.subType == TTF) {
					byte[] data = this.orginTTTables.getTableBytes(OS2);
					// check whether OS2 table exists, other wise create new
					if (data.length > 0) {
						fontData = data;
					}
					else {
						tableWriter = new OS2Writer(this.originalFont, this.glyphs, this.xAvgCharWidth, this.minCharCode, this.maxCharCode,
								this.fontBBox, this.emSquareSize);
					}
				}
				else {
					tableWriter = new OS2Writer(this.originalFont, this.glyphs, this.xAvgCharWidth, this.minCharCode, this.maxCharCode,
							this.fontBBox, this.emSquareSize);
				}
				break;

			case MAXP:
				if (this.subType == TTF) {
					fontData = this.orginTTTables.getTableBytes(MAXP);
				}
				else {
					tableWriter = new MAXPWriter(this.glyphs);
				}
				break;

			case NAME:
				if (this.subType == TTF) {
					// fontData = orginTTTables.getTableBytes(NAME);
					tableWriter = new NameWriter(this.pdfFont, this.glyphs, this.name);
				}
				else {
					tableWriter = new NameWriter(this.pdfFont, this.glyphs, this.name);
				}
				break;

			case POST:
				if (this.subType == TTF) {
					// check whether the POST table exists, other wise create new
					byte[] data = this.orginTTTables.getTableBytes(POST);
					if (data.length > 0) {
						fontData = data;
					}
					else {
						tableWriter = new PostWriter();
					}
				}
				else {
					tableWriter = new PostWriter();
				}
				break;

			// new additions START
			case PREP:
				fontData = this.orginTTTables.getTableBytes(PREP);
				// fill with zero values if no bytes found
				if (fontData.length == 0) {
					fontData = new byte[] { FontWriter.setNextUint8(0) };
				}
				break;

			case CVT:
				fontData = this.orginTTTables.getTableBytes(CVT);
				// fill with zero values if no bytes found; cvt values always has to be double bytes
				if (fontData.length == 0) {
					fontData = FontWriter.setNextUint16(0);
				}
				break;

			case FPGM:
				fontData = this.orginTTTables.getTableBytes(FPGM);
				// fill with zero values if no bytes found
				if (fontData.length == 0) {
					fontData = new byte[] { FontWriter.setNextUint8(0) };
				}
				break;
			// new additions END

			default:

				// //@sam - code to hack table from manulaly converted
				// // font (setup fro Zapf)
				// if(name.contains("BKJFHK+HermesFB-Regular")){
				// // if(name.contains("apf")){
				//
				// try {
				// File file=new File("C:/Users/Sam/Downloads/BKJFHK+HermesFB-Regular.otf");
				// // File file=new File("C:/Users/Sam/Downloads/zapfechosWorks.otf");
				// int len=(int)file.length();
				// byte[] data=new byte[len];
				// FileInputStream fis=new FileInputStream(file);
				// fis.read(data);
				// fis.close();
				// FontFile2 f=new FontFile2(data,false);
				// f.selectTable(tableID);
				// fontData=f.getTableBytes(tableID);
				// }
				// }

				break;
		}

		if (tableWriter != null) {
			try {
				fontData = tableWriter.writeTable();
			}
			catch (Exception e) {
				// tell user and log
				if (LogWriter.isOutput()) LogWriter.writeLog("Exception: " + e.getMessage());
			}
		}

		// Code to save out table
		// if (tableID == CFF && name.contains("NAAAFM"))
		// new BinaryTool(fontData,"C:/Users/sam/desktop/iab/"+name+".iab");

		return fontData;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy