
org.jpedal.fonts.glyph.T1Glyphs Maven / Gradle / Ivy
/*
* ===========================================
* 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-2016 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
*
* ---------------
* T1Glyphs.java
* ---------------
*/
package org.jpedal.fonts.glyph;
import java.util.HashMap;
import java.util.Map;
import java.io.BufferedReader;
import java.io.StringReader;
import java.io.IOException;
import java.util.Collections;
import org.jpedal.fonts.StandardFonts;
import org.jpedal.fonts.glyph.objects.T1GlyphNumber;
import org.jpedal.fonts.objects.FontData;
import org.jpedal.render.DynamicVectorRenderer;
import org.jpedal.utils.LogWriter;
public class T1Glyphs extends PdfJavaGlyphs {
//flag to show if actually 1c
private boolean is1C;
private String[] charForGlyphIndex;
private DynamicVectorRenderer dynamicVectorRenderer;
/**holds mappings for drawing the glpyhs*/
private final Map charStrings=new HashMap();
private final Map glyphNumbers=new HashMap();
/**holds the numbers*/
private static final int max=100;
private double[] operandsRead = new double[max];
/**pointer on stack*/
private int operandReached;
private float[] pt;
//co-ords for closing glyphs
private double xs=-1,ys=-1,x,y;
/**tracks points read in t1 flex*/
private int ptCount;
/**op to be used next*/
private int currentOp;
/**used to count up hints*/
private int hintCount;
/** I byte ops in CFF DIct table *
* private static String[] raw1ByteValues =
* {
* "version",
* "Notice",
* "FullName",
* "FamilyName",
* "Weight",
* "FontBBox",
* "BlueValues",
* "OtherBlues",
* "FamilyBlues",
* "FamilyOtherBlues",
* "StdHW",
* "StdVW",
* "escape",
* "UniqueID",
* "XUID",
* "charset",
* "Encoding",
* "CharStrings",
* "Private",
* "Subrs",
* "defaultWidthX",
* "nominalWidthX",
* "-Reserved-",
* "-Reserved-",
* "-Reserved-",
* "-Reserved-",
* "-Reserved-",
* "-Reserved-",
* "intint",
* "longint",
* "BCD",
* "-Reserved-" };/**/
/** 2 byte ops in CFF DIct table *
* private static String[] raw2ByteValues =
* {
* "Copyright",
* "isFixedPitch",
* "ItalicAngle",
* "UnderlinePosition",
* "UnderlineThickness",
* "PaintType",
* "CharstringType",
* "FontMatrix",
* "StrokeWidth",
* "BlueScale",
* "BlueShift",
* "BlueFuzz",
* "StemSnapH",
* "StemSnapV",
* "ForceBold",
* "-Reserved-",
* "-Reserved-",
* "LanguageGroup",
* "ExpansionFactor",
* "initialRandomSeed",
* "SyntheticBase",
* "PostScript",
* "BaseFontName",
* "BaseFontBlend",
* "-Reserved-",
* "-Reserved-",
* "-Reserved-",
* "-Reserved-",
* "-Reserved-",
* "-Reserved-",
* "ROS",
* "CIDFontVersion",
* "CIDFontRevision",
* "CIDFontType",
* "CIDCount",
* "UIDBase",
* "FDArray",
* "FDSelect",
* "FontName" };/**/
/**used by t1 font renderer to ensure hsbw or sbw executed first*/
private boolean allowAll;
private double h;
private boolean isCID;
//Used by font code - base width to add offset to
private int[] nominalWidthX = {0}, defaultWidthX = {};
private boolean defaultWidthsPassed;
private int[] fdSelect;
public T1Glyphs(final boolean isCID) {
this.isCID=isCID;
}
/**used by PS2OTF conversion*/
@SuppressWarnings("UnusedParameters")
public T1Glyphs(final boolean isCID, final boolean is1C) {
this.charForGlyphIndex=new String[65536];
}
/**
* return name of font
* 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
* @param fontData
*/
public static String[] readFontNames(final FontData fontData) {
final String[] fontNames=new String[1];
fontNames[0]=null;
final BufferedReader br =new BufferedReader(new StringReader(new String(fontData.getBytes(0,fontData.length()))));
String line=null;
while (true) {
try {
line = br.readLine();
} catch (final IOException e) {
LogWriter.writeLog("Exception: " + e.getMessage());
}
if (line == null) {
break;
}
if (line.startsWith("/FontName")){
final int nameStart=line.indexOf('/',9);
if(nameStart!=-1){
final int nameEnd=line.indexOf(' ', nameStart);
if(nameEnd!=-1){
final String name=line.substring(nameStart+1,nameEnd);
fontNames[0]=name.toLowerCase();
break;
}
}
}
}
if(br!=null){
try{
br.close();
}catch (final Exception e) {
LogWriter.writeLog("Exception " + e + " closing stream");
}
}
if(fontData!=null) {
fontData.close();
}
return fontNames;
}
/**
* @param factory
* @param isFlex
* @param routine
*/
private boolean processFlex(final GlyphFactory factory, boolean isFlex, final int routine) {
//if in flex feature see if we have all values - exit if not
if((isFlex)&&(ptCount==14)&&(routine==0)){
isFlex=false;
for(int i=0;i<12;i += 6){
factory.curveTo(pt[i],pt[i+1],pt[i+2],pt[i+3],pt[i+4],pt[i+5]);
}
}else if((!isFlex)&&(routine>=0)&&(routine<=2)){ //determine if flex feature and enable
isFlex=true;
ptCount=0;
pt=new float[16];
}
return isFlex;
}
/**
* @param factory
* @param rawInt
*/
private void endchar(final GlyphFactory factory, final int rawInt) {
if(operandReached==5){ //allow for width and 4 chars
operandReached--;
currentOp++;
}
if(operandReached==4){
StandardFonts.checkLoaded(StandardFonts.STD);
final float adx=(float)(x+operandsRead[currentOp]);
final float ady=(float)(y+operandsRead[currentOp+1]);
final String bchar=StandardFonts.getUnicodeChar(StandardFonts.STD ,(int)operandsRead[currentOp+2]);
final String achar=StandardFonts.getUnicodeChar(StandardFonts.STD ,(int)operandsRead[currentOp+3]);
x=0;
y=0;
decodeGlyph(null,factory,bchar,rawInt, "");
factory.closePath();
factory.moveTo(adx,ady);
x=adx;
y=ady;
decodeGlyph(null,factory,achar,rawInt, "");
if(xs==-1){
xs=x;
ys=y;
}
}else {
factory.closePath();
}
}
/**
* @param p
*/
private int mask(int p) {
hintCount+=operandReached/2;
int count=hintCount;
while(count>0){
p++;
count -= 8;
}
return p;
}
/**
*
*/
private double sbw() {
final double yy;
double val=operandsRead[operandReached-2];
y=val;
val=operandsRead[operandReached-1];
x=val;
xs=x;
ys=y;
allowAll=true;
yy=y;
h=operandsRead[operandReached-3];
return yy;
}
/**
* @param factory
* @param isFirst
*/
private void hmoveto(final GlyphFactory factory, final boolean isFirst) {
if(isFirst && operandReached==2) {
currentOp++;
}
final double val=operandsRead[currentOp];
x += val;
factory.moveTo((float)x,(float)y);
xs=x;
ys=y;
}
/**
* @param factory
* @param isFirst
*/
private void rmoveto(final GlyphFactory factory, final boolean isFirst) {
if((isFirst)&&(operandReached==3)) {
currentOp++;
}
double val=operandsRead[currentOp+1];
y += val;
val=operandsRead[currentOp];
x += val;
factory.moveTo((float)x,(float)y);
//if(xs==-1){
xs=x;
ys=y;
}
/**
* @param factory
* @param key
*
*/
private void vhhvcurveto(final GlyphFactory factory, final int key) {
boolean isHor=(key==31);
while ( operandReached >= 4 ){
operandReached -= 4;
if ( isHor ) {
x += operandsRead[currentOp];
} else {
y += operandsRead[currentOp];
}
pt[0]=(float) x;
pt[1]=(float) y;
x += operandsRead[currentOp+1];
y += operandsRead[currentOp+2];
pt[2]=(float) x;
pt[3]=(float) y;
if ( isHor ){
y += operandsRead[currentOp+3];
if ( operandReached ==1 ) {
x += operandsRead[currentOp + 4];
}
}else{
x += operandsRead[currentOp+3];
if ( operandReached == 1 ) {
y += operandsRead[currentOp + 4];
}
}
pt[4]=(float) x;
pt[5]=(float) y;
factory.curveTo(pt[0],pt[1],pt[2],pt[3],pt[4],pt[5]);
currentOp += 4;
isHor = !isHor;
}
}
/**
* @param factory
* @param key
*/
private void vvhhcurveto(final GlyphFactory factory, final int key) {
final boolean isVV=(key==26);
if ( (operandReached & 1) ==1 ){
if(isVV) {
x += operandsRead[0];
} else {
y += operandsRead[0];
}
currentOp++;
}
//note odd co-ord order
while (currentOp 0 ){
x += operandsRead[currentOp];
y += operandsRead[currentOp+1];
factory.lineTo((float)x,(float)y);
currentOp += 2;
lineCount--;
}
//curves
final float[] coords=new float[6];
x += operandsRead[currentOp];
y += operandsRead[currentOp+1];
coords[0]=(float) x;
coords[1]=(float) y;
x += operandsRead[currentOp+2];
y += operandsRead[currentOp+3];
coords[2]=(float) x;
coords[3]=(float) y;
x += operandsRead[currentOp+4];
y += operandsRead[currentOp+5];
coords[4]=(float) x;
coords[5]=(float) y;
factory.curveTo(coords[0],coords[1],coords[2],coords[3],coords[4],coords[5]);
currentOp += 6;
}
/**
* @param factory
*/
private void closepath(final GlyphFactory factory) {
if(xs!=-1) {
factory.lineTo((float) xs, (float) ys);
}
xs=-1; //flag as unset
}
/**
* @param factory
*/
private void hsbw(final GlyphFactory factory, final String glyphName) {
x += operandsRead[0];
factory.moveTo((float)x,0);
if (baseFontName != null && //Check right call
dynamicVectorRenderer != null && //Check right call
dynamicVectorRenderer.isHTMLorSVG()) { //Just to be safe
dynamicVectorRenderer.saveAdvanceWidth(baseFontName,glyphName,(int)ys);
}
allowAll=true;
}
/**
*
*/
private void pop() {
if(operandReached>0) {
operandReached--;
}
}
/**
*
*/
private void div() {
final double value=operandsRead[operandReached-2]/operandsRead[operandReached-1];
//operandReached--;
if(operandReached>0) {
operandReached--;
}
operandsRead[operandReached-1]=value;
}
/**
* @param factory
* @param isFirst
*/
private void vmoveto(final GlyphFactory factory, final boolean isFirst) {
if((isFirst)&&(operandReached==2)) {
currentOp++;
}
y += operandsRead[currentOp];
factory.moveTo((float)x,(float)y);
//if((xs==-1)){
xs=x;
ys=y;
}
/**
* @param factory
*/
private void rlineto(final GlyphFactory factory) {
int lineCount=operandReached/2;
while ( lineCount > 0 ){
x += operandsRead[currentOp];
y += operandsRead[currentOp+1];
factory.lineTo((float)x,(float)y);
currentOp += 2;
lineCount--;
}
}
/**
* @param factory
* @param key
*/
private void hvlineto(final GlyphFactory factory, final int key) {
boolean isHor = ( key==6 );
int start=0;
while (start 0 ){
final float[] coords=new float[6];
x += operandsRead[currentOp];
y += operandsRead[currentOp+1];
coords[0]=(float) x;
coords[1]=(float) y;
x += operandsRead[currentOp+2];
y += operandsRead[currentOp+3];
coords[2]=(float) x;
coords[3]=(float) y;
x += operandsRead[currentOp+4];
y += operandsRead[currentOp+5];
coords[4]=(float) x;
coords[5]=(float) y;
factory.curveTo(coords[0],coords[1],coords[2],coords[3],coords[4],coords[5]);
currentOp += 6;
curveCount--;
}
}
/**
* @param factory
*/
private void rcurveline(final GlyphFactory factory) {
//curves
int curveCount=( operandReached - 2 ) / 6;
while ( curveCount > 0 ){
final float[] coords=new float[6];
x += operandsRead[currentOp];
y += operandsRead[currentOp+1];
coords[0]=(float) x;
coords[1]=(float) y;
x += operandsRead[currentOp+2];
y += operandsRead[currentOp+3];
coords[2]=(float) x;
coords[3]=(float) y;
x += operandsRead[currentOp+4];
y += operandsRead[currentOp+5];
coords[4]=(float) x;
coords[5]=(float) y;
factory.curveTo(coords[0],coords[1],coords[2],coords[3],coords[4],coords[5]);
currentOp += 6;
curveCount--;
}
// line
x += operandsRead[currentOp];
y += operandsRead[currentOp+1];
factory.lineTo((float)x,(float)y);
currentOp += 2;
}
/**
* @param factory
* @param rawInt
* @param currentOp
*/
private void seac(final GlyphFactory factory, final int rawInt, final int currentOp) {
StandardFonts.checkLoaded(StandardFonts.STD);
final float adx=(float)(operandsRead[currentOp+1]);
final float ady=(float)(operandsRead[currentOp+2]);
final String bchar=StandardFonts.getUnicodeChar(StandardFonts.STD ,(int)operandsRead[currentOp+3]);
final String achar=StandardFonts.getUnicodeChar(StandardFonts.STD ,(int)operandsRead[currentOp+4]);
final double preX=x;
//x=0;
y=0;
decodeGlyph(null,factory,bchar,rawInt, "");
factory.closePath();
factory.moveTo(0,0);
x=adx+preX;
y=ady;
decodeGlyph(null,factory,achar,rawInt, "");
}
/**
* @param factory
*/
private void flex1(final GlyphFactory factory) {
double dx = 0;
double dy = 0;
final double x1=x;
final double y1=y;
/*workout dx/dy/horizontal and reset flag*/
for ( int count =0; count <10; count += 2 ){
dx += operandsRead[count];
dy += operandsRead[count+1];
}
final boolean isHorizontal=(Math.abs(dx)>Math.abs(dy));
for(int points=0;points<6;points += 2){//first curve
x += operandsRead[points];
y += operandsRead[points+1];
pt[points]=(float) x;
pt[points+1]=(float) y;
}
factory.curveTo(pt[0], pt[1], pt[2], pt[3], pt[4],pt[5]);
for(int points=0;points<4;points += 2){//second curve
x += operandsRead[points+6];
y += operandsRead[points+7];
pt[points]=(float) x;
pt[points+1]=(float) y;
}
if ( isHorizontal ){ // last point
x += operandsRead[10];
y = y1;
}else{
x = x1;
y += operandsRead[10];
}
pt[4]=(float) x;
pt[5]=(float) y;
factory.curveTo(pt[0], pt[1], pt[2], pt[3], pt[4],pt[5]);
}
/**
* @param factory
*/
private void flex(final GlyphFactory factory) {
for(int curves=0;curves<12;curves += 6){
for(int points=0;points<6;points += 2){
x += operandsRead[curves+points];
y += operandsRead[curves+points+1];
pt[points]=(float) x;
pt[points+1]=(float) y;
}
factory.curveTo(pt[0],pt[1],pt[2],pt[3],pt[4],pt[5]);
}
}
/**
* @param factory
*/
private void hflex(final GlyphFactory factory) {
//first curve
x += operandsRead[0];
pt[0]=(float) x;
pt[1]=(float) y;
x += operandsRead[1];
y += operandsRead[2];
pt[2]=(float) x;
pt[3]=(float) y;
x += operandsRead[3];
pt[4]=(float) x;
pt[5]=(float) y;
factory.curveTo(pt[0],pt[1],pt[2],pt[3],pt[4],pt[5]);
//second curve
x += operandsRead[4];
pt[0]=(float) x;
pt[1]=(float) y;
x += operandsRead[5];
pt[2]=(float) x;
pt[3]=(float) y;
x += operandsRead[6];
pt[4]=(float) x;
pt[5]=(float) y;
factory.curveTo(pt[0],pt[1],pt[2],pt[3],pt[4],pt[5]);
}
/**
* @param factory
*/
private void hflex1(final GlyphFactory factory) {
//first curve
x+=operandsRead[0];
y+=operandsRead[1];
pt[0]=(float) x;
pt[1]=(float) y;
x+=operandsRead[2];
y+=operandsRead[3];
pt[2]=(float) x;
pt[3]=(float) y;
x+=operandsRead[4];
pt[4]=(float) x;
pt[5]=(float) y;
factory.curveTo(pt[0],pt[1],pt[2],pt[3],pt[4],pt[5]);
//second curve
x+=operandsRead[5];
pt[0]=(float) x;
pt[1]=(float) y;
x+=operandsRead[6];
y+=operandsRead[7];
pt[2]=(float) x;
pt[3]=(float) y;
x += operandsRead[8];
pt[4]=(float) x;
pt[5]=(float) y;
factory.curveTo(pt[0],pt[1],pt[2],pt[3],pt[4],pt[5]);
}
/**
* used by non type3 font
*/
@Override
public PdfGlyph getEmbeddedGlyph(final GlyphFactory factory, final String glyph, final float[][] Trm, final int rawInt,
final String displayValue, final float currentWidth, final String key) {
/**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(rawInt);
if (transformedGlyph2 == null) {
/**create new stack for glyph*/
operandsRead = new double[max];
operandReached=0;
x=-factory.getLSB();
y=0;
decodeGlyph(key,factory,glyph,rawInt, displayValue);
//generate Glyph
transformedGlyph2=factory.getGlyph();
//save so we can reuse if it occurs again in this TJ command
setEmbeddedCachedShape(rawInt, transformedGlyph2);
}
/**
* Save glyph number into object
*/
//Try fetching number using glyph
Object num = glyphNumbers.get(glyph);
//Failing that, use rawInt
if (num == null) {
num = glyphNumbers.get(Integer.toString(rawInt));
}
//Failing that, use key
if (num == null) {
num = glyphNumbers.get(key);
}
//If the glyph number has been found, store it in the glyph
if (num != null) {
transformedGlyph2.setGlyphNumber((Integer) num);
}
return transformedGlyph2;
}
/*
* creates glyph from type1c font commands
*/
void decodeGlyph(final String embKey, final GlyphFactory factory, String glyph, final int rawInt, final String displayValue) {
byte[] glyphStream;
//System.out.println(glyph+" "+baseFontName+" "+rawInt+" "+currentWidth);
allowAll=false; //used by T1 to make sure sbw of hsbw
/**
* get the stream of commands for the glyph
*/
if(isCID){
glyphStream= charStrings.get(String.valueOf(rawInt));
}else{
if(glyph==null) {
glyph = displayValue;//getMappedChar(rawInt,false);
}
if(glyph==null){
glyph=embKey;
if(glyph==null) {
glyph = ".notdef";
}
}
/**
* get the bytestream of commands and reset global values
*/
glyphStream = charStrings.get(glyph);
if(glyphStream==null){
if(embKey!=null) {
glyphStream = charStrings.get(embKey);
}
if(glyphStream==null) {
glyphStream = charStrings.get(".notdef");
}
}
}
/**
* if valid stream then decode
*/
if(glyphStream!=null){
decodeGlyphStream(factory, glyph, rawInt, glyphStream);
}
}
private void decodeGlyphStream(final GlyphFactory factory, final String glyph, final int rawInt, byte[] glyphStream) {
boolean isFirst=true; //flag to pick up extra possible first value
ptCount=0;
int nonSubrCommandCount=0; //command number ignoring subr commands
int p = 0,lastNumberStart=0, nextVal,key=0,lastKey,dicEnd=glyphStream.length,lastVal=0;
currentOp=0;
hintCount=0;
double ymin=999999,ymax=0,yy=1000;
boolean isFlex=false; //flag to show its a flex command in t1
pt=new float[6];
int potentialWidth = 0;
h=100000;
/**set length for 1C*/
if(is1C){
operandsRead=new double[max];
operandReached=0;
allowAll=true;
}
/**
* work through the commands decoding and extracting numbers (operands are FIRST)
*/
while (p < dicEnd) {
//get next byte value from stream
nextVal = glyphStream[p] & 0xFF;
if (nextVal >31 || nextVal==28) { //if its a number get it and update pointer p
//track location
lastNumberStart=p;
//isNumber=true;
p= T1GlyphNumber.getNumber(glyphStream, p,operandsRead,operandReached,is1C);
lastVal=(int) operandsRead[operandReached];//nextVal;
operandReached++;
//Pick up if first item is a number as this may be the width offset - currently only used for HTML
if (lastNumberStart == 0) {
if (nominalWidthX.length == 1) {
potentialWidth = nominalWidthX[0]+lastVal;
} else {
final int glyphNo = glyphNumbers.get(String.valueOf(rawInt))-1;
if (glyphNo < fdSelect.length) {
potentialWidth = nominalWidthX[fdSelect[glyphNo]]+lastVal;
}
}
}
}else{ // operator
//This tests whether the previously saved potential width is an argument of the first operator or
//an actual width. If it's an actual width, it's saved.
if (is1C && nonSubrCommandCount == 0 &&
nextVal != 10 && //callsubr
nextVal != 11 && //return
nextVal != 29) { //callgsubr
boolean hasOddArgs=false;
if (nextVal == 22 || //hmoveto
nextVal == 4 || //vmoveto
(nextVal == 12 && (
glyphStream[p+1] == 9 || //abs
glyphStream[p+1] == 14 || //neg
glyphStream[p+1] == 26 || //sqrt
glyphStream[p+1] == 18 || //drop
glyphStream[p+1] == 27 || //dup
glyphStream[p+1] == 21 || //get
glyphStream[p+1] == 5))) { //not
hasOddArgs = true;
}
if (((!hasOddArgs && (operandReached % 2 == 1)) || (hasOddArgs && (operandReached % 2 == 0)))) { //Make sure not an argument
saveWidth(glyph, rawInt, potentialWidth);
}
}
nonSubrCommandCount++;
//isNumber=false;
lastKey=key;
key = nextVal;
p++;
currentOp=0;
if (key ==12) { //handle escaped keys (ie 2 byte ops)
key= glyphStream[p] & 0xFF;
p++;
if(key==7){ //sbw
yy = sbw();
operandReached=0; //move to first operator
}else if((key == 16 && allowAll) || key != 16){ //other 2 byte operands
isFlex = handle2ByteOp(factory, rawInt, key, lastVal, isFlex);
}
}else if(key==13){ //hsbw (T1 only)
hsbw(factory, glyph);
operandReached=0; //move to first operator
} else if(allowAll){ //other one byte ops
if(key==0){ //reserved
}else if(key==1 || key==3 || key==18 || key==23){ //hstem vstem hstemhm vstemhm
hintCount+=operandReached/2;
operandReached=0; //move to first operator
}else if(key==4){ //vmoveto
if(isFlex){
flex();
}else {
vmoveto(factory, isFirst);
}
operandReached=0; //move to first operator
}else if(key==5){//rlineto
rlineto(factory);
operandReached=0; //move to first operator
} else if(key==6 || key==7){//hlineto or vlineto
hvlineto(factory, key);
operandReached=0; //move to first operator
}else if(key==8){//rrcurveto
rrcurveto(factory);
operandReached=0; //move to first operator
}else if(key==9){ //closepath (T1 only)
closepath(factory);
operandReached=0; //move to first operator
}else if(key==10 || (key==29)){ //callsubr and callgsubr
nonSubrCommandCount--;
if(!is1C && key==10 && (lastVal>=0)&&(lastVal<=2) && lastKey!=11 && operandReached>5){//last key stops spurious match in multiple sub-routines
isFlex = processFlex(factory, isFlex, lastVal);
operandReached=0; //move to first operator
}else{
// factor in bias
if(key==10) {
lastVal += localBias;
} else {
lastVal += globalBias;
}
final byte[] newStream;
if(key==10){ //local subroutine
newStream = charStrings.get("subrs"+ (lastVal));
}else{ //global subroutine
newStream = charStrings.get("global"+ (lastVal));
}
if(newStream!=null){
final int newLength=newStream.length;
final int oldLength=glyphStream.length;
final int totalLength=newLength+oldLength-2;
dicEnd=dicEnd+newLength-2;
//workout length of new stream
final byte[] combinedStream=new byte[totalLength];
System.arraycopy(glyphStream, 0, combinedStream, 0, lastNumberStart);
System.arraycopy(newStream, 0, combinedStream, lastNumberStart, newLength);
System.arraycopy(glyphStream, p, combinedStream, lastNumberStart+newLength, oldLength-p);
glyphStream=combinedStream;
p=lastNumberStart;
if(operandReached>0) {
operandReached--;
}
}
}
//operandReached=0; //move to first operator
}else if(key==11) { //return
nonSubrCommandCount--;
//operandReached=0; //move to first operator
}else if((key==14)){ //endchar
endchar(factory, rawInt);
operandReached=0; //move to first operator
p=dicEnd+1;
}else if(key==16) { //blend
operandReached=0; //move to first operator
}else if((key==19 || key==20)){ //hintmask //cntrmask
p = mask( p);
operandReached=0; //move to first operator
}else if(key==21){//rmoveto
if(isFlex){
moveToAsFlex();
}else {
rmoveto(factory, isFirst);
}
operandReached=0; //move to first operator
}else if(key==22){ //hmoveto
if(isFlex){
final double val=operandsRead[currentOp];
x += val;
pt[ptCount]=(float) x;
ptCount++;
pt[ptCount]=(float) y;
ptCount++;
}else {
hmoveto(factory, isFirst);
}
operandReached=0; //move to first operator
}else if(key==24){ //rcurveline
rcurveline(factory);
operandReached=0; //move to first operator
}else if(key==25){ //rlinecurve
rlinecurve(factory);
operandReached=0; //move to first operator
}else if(key==26 || key==27){ //vvcurve hhcurveto
vvhhcurveto(factory, key);
operandReached=0; //move to first operator
}else if(key==30 || key==31){ //vhcurveto/hvcurveto
vhhvcurveto(factory, key);
operandReached=0; //move to first operator
}
}
if(ymin>y) {
ymin = y;
}
if(ymaxh) {
ymin = yy - h;
}
if(ymax
© 2015 - 2025 Weber Informatics LLC | Privacy Policy