Subversion Repositories Kolibri OS

Compare Revisions

Regard whitespace Rev 4679 → Rev 4680

/contrib/media/updf/pdf/data_encodings.h
0,0 → 1,214
#define _notdef NULL
 
const unsigned short pdf_doc_encoding[256] =
{
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0009, 0x000A, 0x0000, 0x0000, 0x000D, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x02d8, 0x02c7, 0x02c6, 0x02d9, 0x02dd, 0x02db, 0x02da, 0x02dc,
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x0000,
0x2022, 0x2020, 0x2021, 0x2026, 0x2014, 0x2013, 0x0192, 0x2044,
0x2039, 0x203a, 0x2212, 0x2030, 0x201e, 0x201c, 0x201d, 0x2018,
0x2019, 0x201a, 0x2122, 0xfb01, 0xfb02, 0x0141, 0x0152, 0x0160,
0x0178, 0x017d, 0x0131, 0x0142, 0x0153, 0x0161, 0x017e, 0x0000,
0x20ac, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,
0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x0000, 0x00ae, 0x00af,
0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,
0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,
0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,
0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,
0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7,
0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff
};
 
const char * const pdf_standard[256] = { _notdef, _notdef,
_notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
_notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
_notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
_notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
_notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
"space", "exclam", "quotedbl", "numbersign", "dollar", "percent",
"ampersand", "quoteright", "parenleft", "parenright", "asterisk",
"plus", "comma", "hyphen", "period", "slash", "zero", "one", "two",
"three", "four", "five", "six", "seven", "eight", "nine", "colon",
"semicolon", "less", "equal", "greater", "question", "at", "A",
"B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
"O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
"bracketleft", "backslash", "bracketright", "asciicircum", "underscore",
"quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
"l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x",
"y", "z", "braceleft", "bar", "braceright", "asciitilde", _notdef,
_notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
_notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
_notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
_notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
_notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
_notdef, _notdef, _notdef, "exclamdown", "cent", "sterling",
"fraction", "yen", "florin", "section", "currency", "quotesingle",
"quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright",
"fi", "fl", _notdef, "endash", "dagger", "daggerdbl", "periodcentered",
_notdef, "paragraph", "bullet", "quotesinglbase", "quotedblbase",
"quotedblright", "guillemotright", "ellipsis", "perthousand",
_notdef, "questiondown", _notdef, "grave", "acute", "circumflex",
"tilde", "macron", "breve", "dotaccent", "dieresis", _notdef,
"ring", "cedilla", _notdef, "hungarumlaut", "ogonek", "caron",
"emdash", _notdef, _notdef, _notdef, _notdef, _notdef,
_notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
_notdef, _notdef, _notdef, _notdef, _notdef, "AE",
_notdef, "ordfeminine", _notdef, _notdef, _notdef, _notdef,
"Lslash", "Oslash", "OE", "ordmasculine", _notdef, _notdef,
_notdef, _notdef, _notdef, "ae", _notdef, _notdef,
_notdef, "dotlessi", _notdef, _notdef, "lslash", "oslash",
"oe", "germandbls", _notdef, _notdef, _notdef, _notdef
};
 
const char * const pdf_mac_roman[256] = { _notdef, _notdef,
_notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
_notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
_notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
_notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
_notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
"space", "exclam", "quotedbl", "numbersign", "dollar", "percent",
"ampersand", "quotesingle", "parenleft", "parenright", "asterisk",
"plus", "comma", "hyphen", "period", "slash", "zero", "one", "two",
"three", "four", "five", "six", "seven", "eight", "nine", "colon",
"semicolon", "less", "equal", "greater", "question", "at", "A",
"B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
"O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
"bracketleft", "backslash", "bracketright", "asciicircum", "underscore",
"grave", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
"l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x",
"y", "z", "braceleft", "bar", "braceright", "asciitilde", _notdef,
"Adieresis", "Aring", "Ccedilla", "Eacute", "Ntilde", "Odieresis",
"Udieresis", "aacute", "agrave", "acircumflex", "adieresis", "atilde",
"aring", "ccedilla", "eacute", "egrave", "ecircumflex", "edieresis",
"iacute", "igrave", "icircumflex", "idieresis", "ntilde", "oacute",
"ograve", "ocircumflex", "odieresis", "otilde", "uacute", "ugrave",
"ucircumflex", "udieresis", "dagger", "degree", "cent", "sterling",
"section", "bullet", "paragraph", "germandbls", "registered",
"copyright", "trademark", "acute", "dieresis", _notdef, "AE",
"Oslash", _notdef, "plusminus", _notdef, _notdef, "yen", "mu",
_notdef, _notdef, _notdef, _notdef, _notdef, "ordfeminine",
"ordmasculine", _notdef, "ae", "oslash", "questiondown", "exclamdown",
"logicalnot", _notdef, "florin", _notdef, _notdef, "guillemotleft",
"guillemotright", "ellipsis", "space", "Agrave", "Atilde", "Otilde",
"OE", "oe", "endash", "emdash", "quotedblleft", "quotedblright",
"quoteleft", "quoteright", "divide", _notdef, "ydieresis",
"Ydieresis", "fraction", "currency", "guilsinglleft", "guilsinglright",
"fi", "fl", "daggerdbl", "periodcentered", "quotesinglbase",
"quotedblbase", "perthousand", "Acircumflex", "Ecircumflex", "Aacute",
"Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis", "Igrave",
"Oacute", "Ocircumflex", _notdef, "Ograve", "Uacute", "Ucircumflex",
"Ugrave", "dotlessi", "circumflex", "tilde", "macron", "breve",
"dotaccent", "ring", "cedilla", "hungarumlaut", "ogonek", "caron"
};
 
const char * const pdf_mac_expert[256] = { _notdef, _notdef,
_notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
_notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
_notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
_notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
_notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
"space", "exclamsmall", "Hungarumlautsmall", "centoldstyle",
"dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall",
"parenleftsuperior", "parenrightsuperior", "twodotenleader",
"onedotenleader", "comma", "hyphen", "period", "fraction",
"zerooldstyle", "oneoldstyle", "twooldstyle", "threeoldstyle",
"fouroldstyle", "fiveoldstyle", "sixoldstyle", "sevenoldstyle",
"eightoldstyle", "nineoldstyle", "colon", "semicolon", _notdef,
"threequartersemdash", _notdef, "questionsmall", _notdef,
_notdef, _notdef, _notdef, "Ethsmall", _notdef, _notdef,
"onequarter", "onehalf", "threequarters", "oneeighth", "threeeighths",
"fiveeighths", "seveneighths", "onethird", "twothirds", _notdef,
_notdef, _notdef, _notdef, _notdef, _notdef, "ff", "fi",
"fl", "ffi", "ffl", "parenleftinferior", _notdef, "parenrightinferior",
"Circumflexsmall", "hypheninferior", "Gravesmall", "Asmall", "Bsmall",
"Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall", "Ismall",
"Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall",
"Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall",
"Xsmall", "Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah",
"Tildesmall", _notdef, _notdef, "asuperior", "centsuperior",
_notdef, _notdef, _notdef, _notdef, "Aacutesmall",
"Agravesmall", "Acircumflexsmall", "Adieresissmall", "Atildesmall",
"Aringsmall", "Ccedillasmall", "Eacutesmall", "Egravesmall",
"Ecircumflexsmall", "Edieresissmall", "Iacutesmall", "Igravesmall",
"Icircumflexsmall", "Idieresissmall", "Ntildesmall", "Oacutesmall",
"Ogravesmall", "Ocircumflexsmall", "Odieresissmall", "Otildesmall",
"Uacutesmall", "Ugravesmall", "Ucircumflexsmall", "Udieresissmall",
_notdef, "eightsuperior", "fourinferior", "threeinferior",
"sixinferior", "eightinferior", "seveninferior", "Scaronsmall",
_notdef, "centinferior", "twoinferior", _notdef, "Dieresissmall",
_notdef, "Caronsmall", "osuperior", "fiveinferior", _notdef,
"commainferior", "periodinferior", "Yacutesmall", _notdef,
"dollarinferior", _notdef, _notdef, "Thornsmall", _notdef,
"nineinferior", "zeroinferior", "Zcaronsmall", "AEsmall", "Oslashsmall",
"questiondownsmall", "oneinferior", "Lslashsmall", _notdef,
_notdef, _notdef, _notdef, _notdef, _notdef, "Cedillasmall",
_notdef, _notdef, _notdef, _notdef, _notdef, "OEsmall",
"figuredash", "hyphensuperior", _notdef, _notdef, _notdef,
_notdef, "exclamdownsmall", _notdef, "Ydieresissmall", _notdef,
"onesuperior", "twosuperior", "threesuperior", "foursuperior",
"fivesuperior", "sixsuperior", "sevensuperior", "ninesuperior",
"zerosuperior", _notdef, "esuperior", "rsuperior", "tsuperior",
_notdef, _notdef, "isuperior", "ssuperior", "dsuperior",
_notdef, _notdef, _notdef, _notdef, _notdef, "lsuperior",
"Ogoneksmall", "Brevesmall", "Macronsmall", "bsuperior", "nsuperior",
"msuperior", "commasuperior", "periodsuperior", "Dotaccentsmall",
"Ringsmall", _notdef, _notdef, _notdef, _notdef };
 
const char * const pdf_win_ansi[256] = { _notdef, _notdef, _notdef,
_notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
_notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
_notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
_notdef, _notdef, _notdef, _notdef, _notdef, _notdef,
_notdef, _notdef, _notdef, _notdef, _notdef, "space",
"exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand",
"quotesingle", "parenleft", "parenright", "asterisk", "plus",
"comma", "hyphen", "period", "slash", "zero", "one", "two", "three",
"four", "five", "six", "seven", "eight", "nine", "colon", "semicolon",
"less", "equal", "greater", "question", "at", "A", "B", "C", "D",
"E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q",
"R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft",
"backslash", "bracketright", "asciicircum", "underscore", "grave",
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
"n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
"braceleft", "bar", "braceright", "asciitilde", "bullet", "Euro",
"bullet", "quotesinglbase", "florin", "quotedblbase", "ellipsis",
"dagger", "daggerdbl", "circumflex", "perthousand", "Scaron",
"guilsinglleft", "OE", "bullet", "Zcaron", "bullet", "bullet",
"quoteleft", "quoteright", "quotedblleft", "quotedblright", "bullet",
"endash", "emdash", "tilde", "trademark", "scaron", "guilsinglright",
"oe", "bullet", "zcaron", "Ydieresis", "space", "exclamdown", "cent",
"sterling", "currency", "yen", "brokenbar", "section", "dieresis",
"copyright", "ordfeminine", "guillemotleft", "logicalnot", "hyphen",
"registered", "macron", "degree", "plusminus", "twosuperior",
"threesuperior", "acute", "mu", "paragraph", "periodcentered",
"cedilla", "onesuperior", "ordmasculine", "guillemotright",
"onequarter", "onehalf", "threequarters", "questiondown", "Agrave",
"Aacute", "Acircumflex", "Atilde", "Adieresis", "Aring", "AE",
"Ccedilla", "Egrave", "Eacute", "Ecircumflex", "Edieresis", "Igrave",
"Iacute", "Icircumflex", "Idieresis", "Eth", "Ntilde", "Ograve",
"Oacute", "Ocircumflex", "Otilde", "Odieresis", "multiply", "Oslash",
"Ugrave", "Uacute", "Ucircumflex", "Udieresis", "Yacute", "Thorn",
"germandbls", "agrave", "aacute", "acircumflex", "atilde", "adieresis",
"aring", "ae", "ccedilla", "egrave", "eacute", "ecircumflex",
"edieresis", "igrave", "iacute", "icircumflex", "idieresis", "eth",
"ntilde", "ograve", "oacute", "ocircumflex", "otilde", "odieresis",
"divide", "oslash", "ugrave", "uacute", "ucircumflex", "udieresis",
"yacute", "thorn", "ydieresis"
};
/contrib/media/updf/pdf/data_glyphlist.h
0,0 → 1,1461
/*
# Name: Adobe Glyph List
# Table version: 2.0
# Date: September 20, 2002
#
# See http://partners.adobe.com/asn/developer/typeforum/unicodegn.html
#
# Format: Semicolon-delimited fields:
# (1) glyph name
# (2) Unicode scalar value
#--end
*/
 
static const char *agl_name_list[] = {
"A","AE","AEacute","AEmacron","AEsmall","Aacute","Aacutesmall","Abreve",
"Abreveacute","Abrevecyrillic","Abrevedotbelow","Abrevegrave",
"Abrevehookabove","Abrevetilde","Acaron","Acircle","Acircumflex",
"Acircumflexacute","Acircumflexdotbelow","Acircumflexgrave",
"Acircumflexhookabove","Acircumflexsmall","Acircumflextilde","Acute",
"Acutesmall","Acyrillic","Adblgrave","Adieresis","Adieresiscyrillic",
"Adieresismacron","Adieresissmall","Adotbelow","Adotmacron","Agrave",
"Agravesmall","Ahookabove","Aiecyrillic","Ainvertedbreve","Alpha",
"Alphatonos","Amacron","Amonospace","Aogonek","Aring","Aringacute",
"Aringbelow","Aringsmall","Asmall","Atilde","Atildesmall","Aybarmenian","B",
"Bcircle","Bdotaccent","Bdotbelow","Becyrillic","Benarmenian","Beta","Bhook",
"Blinebelow","Bmonospace","Brevesmall","Bsmall","Btopbar","C","Caarmenian",
"Cacute","Caron","Caronsmall","Ccaron","Ccedilla","Ccedillaacute",
"Ccedillasmall","Ccircle","Ccircumflex","Cdot","Cdotaccent","Cedillasmall",
"Chaarmenian","Cheabkhasiancyrillic","Checyrillic",
"Chedescenderabkhasiancyrillic","Chedescendercyrillic","Chedieresiscyrillic",
"Cheharmenian","Chekhakassiancyrillic","Cheverticalstrokecyrillic","Chi",
"Chook","Circumflexsmall","Cmonospace","Coarmenian","Csmall","D","DZ",
"DZcaron","Daarmenian","Dafrican","Dcaron","Dcedilla","Dcircle",
"Dcircumflexbelow","Dcroat","Ddotaccent","Ddotbelow","Decyrillic","Deicoptic",
"Delta","Deltagreek","Dhook","Dieresis","DieresisAcute","DieresisGrave",
"Dieresissmall","Digammagreek","Djecyrillic","Dlinebelow","Dmonospace",
"Dotaccentsmall","Dslash","Dsmall","Dtopbar","Dz","Dzcaron",
"Dzeabkhasiancyrillic","Dzecyrillic","Dzhecyrillic","E","Eacute",
"Eacutesmall","Ebreve","Ecaron","Ecedillabreve","Echarmenian","Ecircle",
"Ecircumflex","Ecircumflexacute","Ecircumflexbelow","Ecircumflexdotbelow",
"Ecircumflexgrave","Ecircumflexhookabove","Ecircumflexsmall",
"Ecircumflextilde","Ecyrillic","Edblgrave","Edieresis","Edieresissmall",
"Edot","Edotaccent","Edotbelow","Efcyrillic","Egrave","Egravesmall",
"Eharmenian","Ehookabove","Eightroman","Einvertedbreve","Eiotifiedcyrillic",
"Elcyrillic","Elevenroman","Emacron","Emacronacute","Emacrongrave",
"Emcyrillic","Emonospace","Encyrillic","Endescendercyrillic","Eng",
"Enghecyrillic","Enhookcyrillic","Eogonek","Eopen","Epsilon","Epsilontonos",
"Ercyrillic","Ereversed","Ereversedcyrillic","Escyrillic",
"Esdescendercyrillic","Esh","Esmall","Eta","Etarmenian","Etatonos","Eth",
"Ethsmall","Etilde","Etildebelow","Euro","Ezh","Ezhcaron","Ezhreversed","F",
"Fcircle","Fdotaccent","Feharmenian","Feicoptic","Fhook","Fitacyrillic",
"Fiveroman","Fmonospace","Fourroman","Fsmall","G","GBsquare","Gacute","Gamma",
"Gammaafrican","Gangiacoptic","Gbreve","Gcaron","Gcedilla","Gcircle",
"Gcircumflex","Gcommaaccent","Gdot","Gdotaccent","Gecyrillic","Ghadarmenian",
"Ghemiddlehookcyrillic","Ghestrokecyrillic","Gheupturncyrillic","Ghook",
"Gimarmenian","Gjecyrillic","Gmacron","Gmonospace","Grave","Gravesmall",
"Gsmall","Gsmallhook","Gstroke","H","H18533","H18543","H18551","H22073",
"HPsquare","Haabkhasiancyrillic","Hadescendercyrillic","Hardsigncyrillic",
"Hbar","Hbrevebelow","Hcedilla","Hcircle","Hcircumflex","Hdieresis",
"Hdotaccent","Hdotbelow","Hmonospace","Hoarmenian","Horicoptic","Hsmall",
"Hungarumlaut","Hungarumlautsmall","Hzsquare","I","IAcyrillic","IJ",
"IUcyrillic","Iacute","Iacutesmall","Ibreve","Icaron","Icircle","Icircumflex",
"Icircumflexsmall","Icyrillic","Idblgrave","Idieresis","Idieresisacute",
"Idieresiscyrillic","Idieresissmall","Idot","Idotaccent","Idotbelow",
"Iebrevecyrillic","Iecyrillic","Ifraktur","Igrave","Igravesmall","Ihookabove",
"Iicyrillic","Iinvertedbreve","Iishortcyrillic","Imacron","Imacroncyrillic",
"Imonospace","Iniarmenian","Iocyrillic","Iogonek","Iota","Iotaafrican",
"Iotadieresis","Iotatonos","Ismall","Istroke","Itilde","Itildebelow",
"Izhitsacyrillic","Izhitsadblgravecyrillic","J","Jaarmenian","Jcircle",
"Jcircumflex","Jecyrillic","Jheharmenian","Jmonospace","Jsmall","K",
"KBsquare","KKsquare","Kabashkircyrillic","Kacute","Kacyrillic",
"Kadescendercyrillic","Kahookcyrillic","Kappa","Kastrokecyrillic",
"Kaverticalstrokecyrillic","Kcaron","Kcedilla","Kcircle","Kcommaaccent",
"Kdotbelow","Keharmenian","Kenarmenian","Khacyrillic","Kheicoptic","Khook",
"Kjecyrillic","Klinebelow","Kmonospace","Koppacyrillic","Koppagreek",
"Ksicyrillic","Ksmall","L","LJ","LL","Lacute","Lambda","Lcaron","Lcedilla",
"Lcircle","Lcircumflexbelow","Lcommaaccent","Ldot","Ldotaccent","Ldotbelow",
"Ldotbelowmacron","Liwnarmenian","Lj","Ljecyrillic","Llinebelow","Lmonospace",
"Lslash","Lslashsmall","Lsmall","M","MBsquare","Macron","Macronsmall",
"Macute","Mcircle","Mdotaccent","Mdotbelow","Menarmenian","Mmonospace",
"Msmall","Mturned","Mu","N","NJ","Nacute","Ncaron","Ncedilla","Ncircle",
"Ncircumflexbelow","Ncommaaccent","Ndotaccent","Ndotbelow","Nhookleft",
"Nineroman","Nj","Njecyrillic","Nlinebelow","Nmonospace","Nowarmenian",
"Nsmall","Ntilde","Ntildesmall","Nu","O","OE","OEsmall","Oacute",
"Oacutesmall","Obarredcyrillic","Obarreddieresiscyrillic","Obreve","Ocaron",
"Ocenteredtilde","Ocircle","Ocircumflex","Ocircumflexacute",
"Ocircumflexdotbelow","Ocircumflexgrave","Ocircumflexhookabove",
"Ocircumflexsmall","Ocircumflextilde","Ocyrillic","Odblacute","Odblgrave",
"Odieresis","Odieresiscyrillic","Odieresissmall","Odotbelow","Ogoneksmall",
"Ograve","Ogravesmall","Oharmenian","Ohm","Ohookabove","Ohorn","Ohornacute",
"Ohorndotbelow","Ohorngrave","Ohornhookabove","Ohorntilde","Ohungarumlaut",
"Oi","Oinvertedbreve","Omacron","Omacronacute","Omacrongrave","Omega",
"Omegacyrillic","Omegagreek","Omegaroundcyrillic","Omegatitlocyrillic",
"Omegatonos","Omicron","Omicrontonos","Omonospace","Oneroman","Oogonek",
"Oogonekmacron","Oopen","Oslash","Oslashacute","Oslashsmall","Osmall",
"Ostrokeacute","Otcyrillic","Otilde","Otildeacute","Otildedieresis",
"Otildesmall","P","Pacute","Pcircle","Pdotaccent","Pecyrillic","Peharmenian",
"Pemiddlehookcyrillic","Phi","Phook","Pi","Piwrarmenian","Pmonospace","Psi",
"Psicyrillic","Psmall","Q","Qcircle","Qmonospace","Qsmall","R","Raarmenian",
"Racute","Rcaron","Rcedilla","Rcircle","Rcommaaccent","Rdblgrave",
"Rdotaccent","Rdotbelow","Rdotbelowmacron","Reharmenian","Rfraktur","Rho",
"Ringsmall","Rinvertedbreve","Rlinebelow","Rmonospace","Rsmall",
"Rsmallinverted","Rsmallinvertedsuperior","S","SF010000","SF020000",
"SF030000","SF040000","SF050000","SF060000","SF070000","SF080000","SF090000",
"SF100000","SF110000","SF190000","SF200000","SF210000","SF220000","SF230000",
"SF240000","SF250000","SF260000","SF270000","SF280000","SF360000","SF370000",
"SF380000","SF390000","SF400000","SF410000","SF420000","SF430000","SF440000",
"SF450000","SF460000","SF470000","SF480000","SF490000","SF500000","SF510000",
"SF520000","SF530000","SF540000","Sacute","Sacutedotaccent","Sampigreek",
"Scaron","Scarondotaccent","Scaronsmall","Scedilla","Schwa","Schwacyrillic",
"Schwadieresiscyrillic","Scircle","Scircumflex","Scommaaccent","Sdotaccent",
"Sdotbelow","Sdotbelowdotaccent","Seharmenian","Sevenroman","Shaarmenian",
"Shacyrillic","Shchacyrillic","Sheicoptic","Shhacyrillic","Shimacoptic",
"Sigma","Sixroman","Smonospace","Softsigncyrillic","Ssmall","Stigmagreek","T",
"Tau","Tbar","Tcaron","Tcedilla","Tcircle","Tcircumflexbelow","Tcommaaccent",
"Tdotaccent","Tdotbelow","Tecyrillic","Tedescendercyrillic","Tenroman",
"Tetsecyrillic","Theta","Thook","Thorn","Thornsmall","Threeroman",
"Tildesmall","Tiwnarmenian","Tlinebelow","Tmonospace","Toarmenian","Tonefive",
"Tonesix","Tonetwo","Tretroflexhook","Tsecyrillic","Tshecyrillic","Tsmall",
"Twelveroman","Tworoman","U","Uacute","Uacutesmall","Ubreve","Ucaron",
"Ucircle","Ucircumflex","Ucircumflexbelow","Ucircumflexsmall","Ucyrillic",
"Udblacute","Udblgrave","Udieresis","Udieresisacute","Udieresisbelow",
"Udieresiscaron","Udieresiscyrillic","Udieresisgrave","Udieresismacron",
"Udieresissmall","Udotbelow","Ugrave","Ugravesmall","Uhookabove","Uhorn",
"Uhornacute","Uhorndotbelow","Uhorngrave","Uhornhookabove","Uhorntilde",
"Uhungarumlaut","Uhungarumlautcyrillic","Uinvertedbreve","Ukcyrillic",
"Umacron","Umacroncyrillic","Umacrondieresis","Umonospace","Uogonek",
"Upsilon","Upsilon1","Upsilonacutehooksymbolgreek","Upsilonafrican",
"Upsilondieresis","Upsilondieresishooksymbolgreek","Upsilonhooksymbol",
"Upsilontonos","Uring","Ushortcyrillic","Usmall","Ustraightcyrillic",
"Ustraightstrokecyrillic","Utilde","Utildeacute","Utildebelow","V","Vcircle",
"Vdotbelow","Vecyrillic","Vewarmenian","Vhook","Vmonospace","Voarmenian",
"Vsmall","Vtilde","W","Wacute","Wcircle","Wcircumflex","Wdieresis",
"Wdotaccent","Wdotbelow","Wgrave","Wmonospace","Wsmall","X","Xcircle",
"Xdieresis","Xdotaccent","Xeharmenian","Xi","Xmonospace","Xsmall","Y",
"Yacute","Yacutesmall","Yatcyrillic","Ycircle","Ycircumflex","Ydieresis",
"Ydieresissmall","Ydotaccent","Ydotbelow","Yericyrillic",
"Yerudieresiscyrillic","Ygrave","Yhook","Yhookabove","Yiarmenian",
"Yicyrillic","Yiwnarmenian","Ymonospace","Ysmall","Ytilde","Yusbigcyrillic",
"Yusbigiotifiedcyrillic","Yuslittlecyrillic","Yuslittleiotifiedcyrillic","Z",
"Zaarmenian","Zacute","Zcaron","Zcaronsmall","Zcircle","Zcircumflex","Zdot",
"Zdotaccent","Zdotbelow","Zecyrillic","Zedescendercyrillic",
"Zedieresiscyrillic","Zeta","Zhearmenian","Zhebrevecyrillic","Zhecyrillic",
"Zhedescendercyrillic","Zhedieresiscyrillic","Zlinebelow","Zmonospace",
"Zsmall","Zstroke","a","aabengali","aacute","aadeva","aagujarati",
"aagurmukhi","aamatragurmukhi","aarusquare","aavowelsignbengali",
"aavowelsigndeva","aavowelsigngujarati","abbreviationmarkarmenian",
"abbreviationsigndeva","abengali","abopomofo","abreve","abreveacute",
"abrevecyrillic","abrevedotbelow","abrevegrave","abrevehookabove",
"abrevetilde","acaron","acircle","acircumflex","acircumflexacute",
"acircumflexdotbelow","acircumflexgrave","acircumflexhookabove",
"acircumflextilde","acute","acutebelowcmb","acutecmb","acutecomb","acutedeva",
"acutelowmod","acutetonecmb","acyrillic","adblgrave","addakgurmukhi","adeva",
"adieresis","adieresiscyrillic","adieresismacron","adotbelow","adotmacron",
"ae","aeacute","aekorean","aemacron","afii00208","afii08941","afii10017",
"afii10018","afii10019","afii10020","afii10021","afii10022","afii10023",
"afii10024","afii10025","afii10026","afii10027","afii10028","afii10029",
"afii10030","afii10031","afii10032","afii10033","afii10034","afii10035",
"afii10036","afii10037","afii10038","afii10039","afii10040","afii10041",
"afii10042","afii10043","afii10044","afii10045","afii10046","afii10047",
"afii10048","afii10049","afii10050","afii10051","afii10052","afii10053",
"afii10054","afii10055","afii10056","afii10057","afii10058","afii10059",
"afii10060","afii10061","afii10062","afii10063","afii10064","afii10065",
"afii10066","afii10067","afii10068","afii10069","afii10070","afii10071",
"afii10072","afii10073","afii10074","afii10075","afii10076","afii10077",
"afii10078","afii10079","afii10080","afii10081","afii10082","afii10083",
"afii10084","afii10085","afii10086","afii10087","afii10088","afii10089",
"afii10090","afii10091","afii10092","afii10093","afii10094","afii10095",
"afii10096","afii10097","afii10098","afii10099","afii10100","afii10101",
"afii10102","afii10103","afii10104","afii10105","afii10106","afii10107",
"afii10108","afii10109","afii10110","afii10145","afii10146","afii10147",
"afii10148","afii10192","afii10193","afii10194","afii10195","afii10196",
"afii10831","afii10832","afii10846","afii299","afii300","afii301","afii57381",
"afii57388","afii57392","afii57393","afii57394","afii57395","afii57396",
"afii57397","afii57398","afii57399","afii57400","afii57401","afii57403",
"afii57407","afii57409","afii57410","afii57411","afii57412","afii57413",
"afii57414","afii57415","afii57416","afii57417","afii57418","afii57419",
"afii57420","afii57421","afii57422","afii57423","afii57424","afii57425",
"afii57426","afii57427","afii57428","afii57429","afii57430","afii57431",
"afii57432","afii57433","afii57434","afii57440","afii57441","afii57442",
"afii57443","afii57444","afii57445","afii57446","afii57448","afii57449",
"afii57450","afii57451","afii57452","afii57453","afii57454","afii57455",
"afii57456","afii57457","afii57458","afii57470","afii57505","afii57506",
"afii57507","afii57508","afii57509","afii57511","afii57512","afii57513",
"afii57514","afii57519","afii57534","afii57636","afii57645","afii57658",
"afii57664","afii57665","afii57666","afii57667","afii57668","afii57669",
"afii57670","afii57671","afii57672","afii57673","afii57674","afii57675",
"afii57676","afii57677","afii57678","afii57679","afii57680","afii57681",
"afii57682","afii57683","afii57684","afii57685","afii57686","afii57687",
"afii57688","afii57689","afii57690","afii57694","afii57695","afii57700",
"afii57705","afii57716","afii57717","afii57718","afii57723","afii57793",
"afii57794","afii57795","afii57796","afii57797","afii57798","afii57799",
"afii57800","afii57801","afii57802","afii57803","afii57804","afii57806",
"afii57807","afii57839","afii57841","afii57842","afii57929","afii61248",
"afii61289","afii61352","afii61573","afii61574","afii61575","afii61664",
"afii63167","afii64937","agrave","agujarati","agurmukhi","ahiragana",
"ahookabove","aibengali","aibopomofo","aideva","aiecyrillic","aigujarati",
"aigurmukhi","aimatragurmukhi","ainarabic","ainfinalarabic",
"aininitialarabic","ainmedialarabic","ainvertedbreve","aivowelsignbengali",
"aivowelsigndeva","aivowelsigngujarati","akatakana","akatakanahalfwidth",
"akorean","alef","alefarabic","alefdageshhebrew","aleffinalarabic",
"alefhamzaabovearabic","alefhamzaabovefinalarabic","alefhamzabelowarabic",
"alefhamzabelowfinalarabic","alefhebrew","aleflamedhebrew",
"alefmaddaabovearabic","alefmaddaabovefinalarabic","alefmaksuraarabic",
"alefmaksurafinalarabic","alefmaksurainitialarabic","alefmaksuramedialarabic",
"alefpatahhebrew","alefqamatshebrew","aleph","allequal","alpha","alphatonos",
"amacron","amonospace","ampersand","ampersandmonospace","ampersandsmall",
"amsquare","anbopomofo","angbopomofo","angkhankhuthai","angle",
"anglebracketleft","anglebracketleftvertical","anglebracketright",
"anglebracketrightvertical","angleleft","angleright","angstrom","anoteleia",
"anudattadeva","anusvarabengali","anusvaradeva","anusvaragujarati","aogonek",
"apaatosquare","aparen","apostrophearmenian","apostrophemod","apple",
"approaches","approxequal","approxequalorimage","approximatelyequal",
"araeaekorean","araeakorean","arc","arighthalfring","aring","aringacute",
"aringbelow","arrowboth","arrowdashdown","arrowdashleft","arrowdashright",
"arrowdashup","arrowdblboth","arrowdbldown","arrowdblleft","arrowdblright",
"arrowdblup","arrowdown","arrowdownleft","arrowdownright","arrowdownwhite",
"arrowheaddownmod","arrowheadleftmod","arrowheadrightmod","arrowheadupmod",
"arrowhorizex","arrowleft","arrowleftdbl","arrowleftdblstroke",
"arrowleftoverright","arrowleftwhite","arrowright","arrowrightdblstroke",
"arrowrightheavy","arrowrightoverleft","arrowrightwhite","arrowtableft",
"arrowtabright","arrowup","arrowupdn","arrowupdnbse","arrowupdownbase",
"arrowupleft","arrowupleftofdown","arrowupright","arrowupwhite","arrowvertex",
"asciicircum","asciicircummonospace","asciitilde","asciitildemonospace",
"ascript","ascriptturned","asmallhiragana","asmallkatakana",
"asmallkatakanahalfwidth","asterisk","asteriskaltonearabic","asteriskarabic",
"asteriskmath","asteriskmonospace","asterisksmall","asterism","asuperior",
"asymptoticallyequal","at","atilde","atmonospace","atsmall","aturned",
"aubengali","aubopomofo","audeva","augujarati","augurmukhi",
"aulengthmarkbengali","aumatragurmukhi","auvowelsignbengali",
"auvowelsigndeva","auvowelsigngujarati","avagrahadeva","aybarmenian","ayin",
"ayinaltonehebrew","ayinhebrew","b","babengali","backslash",
"backslashmonospace","badeva","bagujarati","bagurmukhi","bahiragana",
"bahtthai","bakatakana","bar","barmonospace","bbopomofo","bcircle",
"bdotaccent","bdotbelow","beamedsixteenthnotes","because","becyrillic",
"beharabic","behfinalarabic","behinitialarabic","behiragana",
"behmedialarabic","behmeeminitialarabic","behmeemisolatedarabic",
"behnoonfinalarabic","bekatakana","benarmenian","bet","beta",
"betasymbolgreek","betdagesh","betdageshhebrew","bethebrew","betrafehebrew",
"bhabengali","bhadeva","bhagujarati","bhagurmukhi","bhook","bihiragana",
"bikatakana","bilabialclick","bindigurmukhi","birusquare","blackcircle",
"blackdiamond","blackdownpointingtriangle","blackleftpointingpointer",
"blackleftpointingtriangle","blacklenticularbracketleft",
"blacklenticularbracketleftvertical","blacklenticularbracketright",
"blacklenticularbracketrightvertical","blacklowerlefttriangle",
"blacklowerrighttriangle","blackrectangle","blackrightpointingpointer",
"blackrightpointingtriangle","blacksmallsquare","blacksmilingface",
"blacksquare","blackstar","blackupperlefttriangle","blackupperrighttriangle",
"blackuppointingsmalltriangle","blackuppointingtriangle","blank","blinebelow",
"block","bmonospace","bobaimaithai","bohiragana","bokatakana","bparen",
"bqsquare","braceex","braceleft","braceleftbt","braceleftmid",
"braceleftmonospace","braceleftsmall","bracelefttp","braceleftvertical",
"braceright","bracerightbt","bracerightmid","bracerightmonospace",
"bracerightsmall","bracerighttp","bracerightvertical","bracketleft",
"bracketleftbt","bracketleftex","bracketleftmonospace","bracketlefttp",
"bracketright","bracketrightbt","bracketrightex","bracketrightmonospace",
"bracketrighttp","breve","brevebelowcmb","brevecmb","breveinvertedbelowcmb",
"breveinvertedcmb","breveinverteddoublecmb","bridgebelowcmb",
"bridgeinvertedbelowcmb","brokenbar","bstroke","bsuperior","btopbar",
"buhiragana","bukatakana","bullet","bulletinverse","bulletoperator",
"bullseye","c","caarmenian","cabengali","cacute","cadeva","cagujarati",
"cagurmukhi","calsquare","candrabindubengali","candrabinducmb",
"candrabindudeva","candrabindugujarati","capslock","careof","caron",
"caronbelowcmb","caroncmb","carriagereturn","cbopomofo","ccaron","ccedilla",
"ccedillaacute","ccircle","ccircumflex","ccurl","cdot","cdotaccent",
"cdsquare","cedilla","cedillacmb","cent","centigrade","centinferior",
"centmonospace","centoldstyle","centsuperior","chaarmenian","chabengali",
"chadeva","chagujarati","chagurmukhi","chbopomofo","cheabkhasiancyrillic",
"checkmark","checyrillic","chedescenderabkhasiancyrillic",
"chedescendercyrillic","chedieresiscyrillic","cheharmenian",
"chekhakassiancyrillic","cheverticalstrokecyrillic","chi",
"chieuchacirclekorean","chieuchaparenkorean","chieuchcirclekorean",
"chieuchkorean","chieuchparenkorean","chochangthai","chochanthai",
"chochingthai","chochoethai","chook","cieucacirclekorean","cieucaparenkorean",
"cieuccirclekorean","cieuckorean","cieucparenkorean","cieucuparenkorean",
"circle","circlemultiply","circleot","circleplus","circlepostalmark",
"circlewithlefthalfblack","circlewithrighthalfblack","circumflex",
"circumflexbelowcmb","circumflexcmb","clear","clickalveolar","clickdental",
"clicklateral","clickretroflex","club","clubsuitblack","clubsuitwhite",
"cmcubedsquare","cmonospace","cmsquaredsquare","coarmenian","colon",
"colonmonetary","colonmonospace","colonsign","colonsmall",
"colontriangularhalfmod","colontriangularmod","comma","commaabovecmb",
"commaaboverightcmb","commaaccent","commaarabic","commaarmenian",
"commainferior","commamonospace","commareversedabovecmb","commareversedmod",
"commasmall","commasuperior","commaturnedabovecmb","commaturnedmod","compass",
"congruent","contourintegral","control","controlACK","controlBEL","controlBS",
"controlCAN","controlCR","controlDC1","controlDC2","controlDC3","controlDC4",
"controlDEL","controlDLE","controlEM","controlENQ","controlEOT","controlESC",
"controlETB","controlETX","controlFF","controlFS","controlGS","controlHT",
"controlLF","controlNAK","controlRS","controlSI","controlSO","controlSOT",
"controlSTX","controlSUB","controlSYN","controlUS","controlVT","copyright",
"copyrightsans","copyrightserif","cornerbracketleft",
"cornerbracketlefthalfwidth","cornerbracketleftvertical","cornerbracketright",
"cornerbracketrighthalfwidth","cornerbracketrightvertical",
"corporationsquare","cosquare","coverkgsquare","cparen","cruzeiro",
"cstretched","curlyand","curlyor","currency","cyrBreve","cyrFlex","cyrbreve",
"cyrflex","d","daarmenian","dabengali","dadarabic","dadeva","dadfinalarabic",
"dadinitialarabic","dadmedialarabic","dagesh","dageshhebrew","dagger",
"daggerdbl","dagujarati","dagurmukhi","dahiragana","dakatakana","dalarabic",
"dalet","daletdagesh","daletdageshhebrew","dalethatafpatah",
"dalethatafpatahhebrew","dalethatafsegol","dalethatafsegolhebrew",
"dalethebrew","dalethiriq","dalethiriqhebrew","daletholam","daletholamhebrew",
"daletpatah","daletpatahhebrew","daletqamats","daletqamatshebrew",
"daletqubuts","daletqubutshebrew","daletsegol","daletsegolhebrew",
"daletsheva","daletshevahebrew","dalettsere","dalettserehebrew",
"dalfinalarabic","dammaarabic","dammalowarabic","dammatanaltonearabic",
"dammatanarabic","danda","dargahebrew","dargalefthebrew",
"dasiapneumatacyrilliccmb","dblGrave","dblanglebracketleft",
"dblanglebracketleftvertical","dblanglebracketright",
"dblanglebracketrightvertical","dblarchinvertedbelowcmb","dblarrowleft",
"dblarrowright","dbldanda","dblgrave","dblgravecmb","dblintegral",
"dbllowline","dbllowlinecmb","dbloverlinecmb","dblprimemod","dblverticalbar",
"dblverticallineabovecmb","dbopomofo","dbsquare","dcaron","dcedilla",
"dcircle","dcircumflexbelow","dcroat","ddabengali","ddadeva","ddagujarati",
"ddagurmukhi","ddalarabic","ddalfinalarabic","dddhadeva","ddhabengali",
"ddhadeva","ddhagujarati","ddhagurmukhi","ddotaccent","ddotbelow",
"decimalseparatorarabic","decimalseparatorpersian","decyrillic","degree",
"dehihebrew","dehiragana","deicoptic","dekatakana","deleteleft","deleteright",
"delta","deltaturned","denominatorminusonenumeratorbengali","dezh",
"dhabengali","dhadeva","dhagujarati","dhagurmukhi","dhook","dialytikatonos",
"dialytikatonoscmb","diamond","diamondsuitwhite","dieresis","dieresisacute",
"dieresisbelowcmb","dieresiscmb","dieresisgrave","dieresistonos","dihiragana",
"dikatakana","dittomark","divide","divides","divisionslash","djecyrillic",
"dkshade","dlinebelow","dlsquare","dmacron","dmonospace","dnblock",
"dochadathai","dodekthai","dohiragana","dokatakana","dollar","dollarinferior",
"dollarmonospace","dollaroldstyle","dollarsmall","dollarsuperior","dong",
"dorusquare","dotaccent","dotaccentcmb","dotbelowcmb","dotbelowcomb",
"dotkatakana","dotlessi","dotlessj","dotlessjstrokehook","dotmath",
"dottedcircle","doubleyodpatah","doubleyodpatahhebrew","downtackbelowcmb",
"downtackmod","dparen","dsuperior","dtail","dtopbar","duhiragana",
"dukatakana","dz","dzaltone","dzcaron","dzcurl","dzeabkhasiancyrillic",
"dzecyrillic","dzhecyrillic","e","eacute","earth","ebengali","ebopomofo",
"ebreve","ecandradeva","ecandragujarati","ecandravowelsigndeva",
"ecandravowelsigngujarati","ecaron","ecedillabreve","echarmenian",
"echyiwnarmenian","ecircle","ecircumflex","ecircumflexacute",
"ecircumflexbelow","ecircumflexdotbelow","ecircumflexgrave",
"ecircumflexhookabove","ecircumflextilde","ecyrillic","edblgrave","edeva",
"edieresis","edot","edotaccent","edotbelow","eegurmukhi","eematragurmukhi",
"efcyrillic","egrave","egujarati","eharmenian","ehbopomofo","ehiragana",
"ehookabove","eibopomofo","eight","eightarabic","eightbengali","eightcircle",
"eightcircleinversesansserif","eightdeva","eighteencircle","eighteenparen",
"eighteenperiod","eightgujarati","eightgurmukhi","eighthackarabic",
"eighthangzhou","eighthnotebeamed","eightideographicparen","eightinferior",
"eightmonospace","eightoldstyle","eightparen","eightperiod","eightpersian",
"eightroman","eightsuperior","eightthai","einvertedbreve","eiotifiedcyrillic",
"ekatakana","ekatakanahalfwidth","ekonkargurmukhi","ekorean","elcyrillic",
"element","elevencircle","elevenparen","elevenperiod","elevenroman",
"ellipsis","ellipsisvertical","emacron","emacronacute","emacrongrave",
"emcyrillic","emdash","emdashvertical","emonospace","emphasismarkarmenian",
"emptyset","enbopomofo","encyrillic","endash","endashvertical",
"endescendercyrillic","eng","engbopomofo","enghecyrillic","enhookcyrillic",
"enspace","eogonek","eokorean","eopen","eopenclosed","eopenreversed",
"eopenreversedclosed","eopenreversedhook","eparen","epsilon","epsilontonos",
"equal","equalmonospace","equalsmall","equalsuperior","equivalence",
"erbopomofo","ercyrillic","ereversed","ereversedcyrillic","escyrillic",
"esdescendercyrillic","esh","eshcurl","eshortdeva","eshortvowelsigndeva",
"eshreversedloop","eshsquatreversed","esmallhiragana","esmallkatakana",
"esmallkatakanahalfwidth","estimated","esuperior","eta","etarmenian",
"etatonos","eth","etilde","etildebelow","etnahtafoukhhebrew",
"etnahtafoukhlefthebrew","etnahtahebrew","etnahtalefthebrew","eturned",
"eukorean","euro","evowelsignbengali","evowelsigndeva","evowelsigngujarati",
"exclam","exclamarmenian","exclamdbl","exclamdown","exclamdownsmall",
"exclammonospace","exclamsmall","existential","ezh","ezhcaron","ezhcurl",
"ezhreversed","ezhtail","f","fadeva","fagurmukhi","fahrenheit","fathaarabic",
"fathalowarabic","fathatanarabic","fbopomofo","fcircle","fdotaccent",
"feharabic","feharmenian","fehfinalarabic","fehinitialarabic",
"fehmedialarabic","feicoptic","female","ff","ffi","ffl","fi","fifteencircle",
"fifteenparen","fifteenperiod","figuredash","filledbox","filledrect",
"finalkaf","finalkafdagesh","finalkafdageshhebrew","finalkafhebrew",
"finalkafqamats","finalkafqamatshebrew","finalkafsheva","finalkafshevahebrew",
"finalmem","finalmemhebrew","finalnun","finalnunhebrew","finalpe",
"finalpehebrew","finaltsadi","finaltsadihebrew","firsttonechinese","fisheye",
"fitacyrillic","five","fivearabic","fivebengali","fivecircle",
"fivecircleinversesansserif","fivedeva","fiveeighths","fivegujarati",
"fivegurmukhi","fivehackarabic","fivehangzhou","fiveideographicparen",
"fiveinferior","fivemonospace","fiveoldstyle","fiveparen","fiveperiod",
"fivepersian","fiveroman","fivesuperior","fivethai","fl","florin",
"fmonospace","fmsquare","fofanthai","fofathai","fongmanthai","forall","four",
"fourarabic","fourbengali","fourcircle","fourcircleinversesansserif",
"fourdeva","fourgujarati","fourgurmukhi","fourhackarabic","fourhangzhou",
"fourideographicparen","fourinferior","fourmonospace","fournumeratorbengali",
"fouroldstyle","fourparen","fourperiod","fourpersian","fourroman",
"foursuperior","fourteencircle","fourteenparen","fourteenperiod","fourthai",
"fourthtonechinese","fparen","fraction","franc","g","gabengali","gacute",
"gadeva","gafarabic","gaffinalarabic","gafinitialarabic","gafmedialarabic",
"gagujarati","gagurmukhi","gahiragana","gakatakana","gamma","gammalatinsmall",
"gammasuperior","gangiacoptic","gbopomofo","gbreve","gcaron","gcedilla",
"gcircle","gcircumflex","gcommaaccent","gdot","gdotaccent","gecyrillic",
"gehiragana","gekatakana","geometricallyequal","gereshaccenthebrew",
"gereshhebrew","gereshmuqdamhebrew","germandbls","gershayimaccenthebrew",
"gershayimhebrew","getamark","ghabengali","ghadarmenian","ghadeva",
"ghagujarati","ghagurmukhi","ghainarabic","ghainfinalarabic",
"ghaininitialarabic","ghainmedialarabic","ghemiddlehookcyrillic",
"ghestrokecyrillic","gheupturncyrillic","ghhadeva","ghhagurmukhi","ghook",
"ghzsquare","gihiragana","gikatakana","gimarmenian","gimel","gimeldagesh",
"gimeldageshhebrew","gimelhebrew","gjecyrillic","glottalinvertedstroke",
"glottalstop","glottalstopinverted","glottalstopmod","glottalstopreversed",
"glottalstopreversedmod","glottalstopreversedsuperior","glottalstopstroke",
"glottalstopstrokereversed","gmacron","gmonospace","gohiragana","gokatakana",
"gparen","gpasquare","gradient","grave","gravebelowcmb","gravecmb",
"gravecomb","gravedeva","gravelowmod","gravemonospace","gravetonecmb",
"greater","greaterequal","greaterequalorless","greatermonospace",
"greaterorequivalent","greaterorless","greateroverequal","greatersmall",
"gscript","gstroke","guhiragana","guillemotleft","guillemotright",
"guilsinglleft","guilsinglright","gukatakana","guramusquare","gysquare","h",
"haabkhasiancyrillic","haaltonearabic","habengali","hadescendercyrillic",
"hadeva","hagujarati","hagurmukhi","haharabic","hahfinalarabic",
"hahinitialarabic","hahiragana","hahmedialarabic","haitusquare","hakatakana",
"hakatakanahalfwidth","halantgurmukhi","hamzaarabic","hamzadammaarabic",
"hamzadammatanarabic","hamzafathaarabic","hamzafathatanarabic",
"hamzalowarabic","hamzalowkasraarabic","hamzalowkasratanarabic",
"hamzasukunarabic","hangulfiller","hardsigncyrillic","harpoonleftbarbup",
"harpoonrightbarbup","hasquare","hatafpatah","hatafpatah16","hatafpatah23",
"hatafpatah2f","hatafpatahhebrew","hatafpatahnarrowhebrew",
"hatafpatahquarterhebrew","hatafpatahwidehebrew","hatafqamats",
"hatafqamats1b","hatafqamats28","hatafqamats34","hatafqamatshebrew",
"hatafqamatsnarrowhebrew","hatafqamatsquarterhebrew","hatafqamatswidehebrew",
"hatafsegol","hatafsegol17","hatafsegol24","hatafsegol30","hatafsegolhebrew",
"hatafsegolnarrowhebrew","hatafsegolquarterhebrew","hatafsegolwidehebrew",
"hbar","hbopomofo","hbrevebelow","hcedilla","hcircle","hcircumflex",
"hdieresis","hdotaccent","hdotbelow","he","heart","heartsuitblack",
"heartsuitwhite","hedagesh","hedageshhebrew","hehaltonearabic","heharabic",
"hehebrew","hehfinalaltonearabic","hehfinalalttwoarabic","hehfinalarabic",
"hehhamzaabovefinalarabic","hehhamzaaboveisolatedarabic",
"hehinitialaltonearabic","hehinitialarabic","hehiragana",
"hehmedialaltonearabic","hehmedialarabic","heiseierasquare","hekatakana",
"hekatakanahalfwidth","hekutaarusquare","henghook","herutusquare","het",
"hethebrew","hhook","hhooksuperior","hieuhacirclekorean","hieuhaparenkorean",
"hieuhcirclekorean","hieuhkorean","hieuhparenkorean","hihiragana",
"hikatakana","hikatakanahalfwidth","hiriq","hiriq14","hiriq21","hiriq2d",
"hiriqhebrew","hiriqnarrowhebrew","hiriqquarterhebrew","hiriqwidehebrew",
"hlinebelow","hmonospace","hoarmenian","hohipthai","hohiragana","hokatakana",
"hokatakanahalfwidth","holam","holam19","holam26","holam32","holamhebrew",
"holamnarrowhebrew","holamquarterhebrew","holamwidehebrew","honokhukthai",
"hookabovecomb","hookcmb","hookpalatalizedbelowcmb","hookretroflexbelowcmb",
"hoonsquare","horicoptic","horizontalbar","horncmb","hotsprings","house",
"hparen","hsuperior","hturned","huhiragana","huiitosquare","hukatakana",
"hukatakanahalfwidth","hungarumlaut","hungarumlautcmb","hv","hyphen",
"hypheninferior","hyphenmonospace","hyphensmall","hyphensuperior","hyphentwo",
"i","iacute","iacyrillic","ibengali","ibopomofo","ibreve","icaron","icircle",
"icircumflex","icyrillic","idblgrave","ideographearthcircle",
"ideographfirecircle","ideographicallianceparen","ideographiccallparen",
"ideographiccentrecircle","ideographicclose","ideographiccomma",
"ideographiccommaleft","ideographiccongratulationparen",
"ideographiccorrectcircle","ideographicearthparen",
"ideographicenterpriseparen","ideographicexcellentcircle",
"ideographicfestivalparen","ideographicfinancialcircle",
"ideographicfinancialparen","ideographicfireparen","ideographichaveparen",
"ideographichighcircle","ideographiciterationmark","ideographiclaborcircle",
"ideographiclaborparen","ideographicleftcircle","ideographiclowcircle",
"ideographicmedicinecircle","ideographicmetalparen","ideographicmoonparen",
"ideographicnameparen","ideographicperiod","ideographicprintcircle",
"ideographicreachparen","ideographicrepresentparen",
"ideographicresourceparen","ideographicrightcircle","ideographicsecretcircle",
"ideographicselfparen","ideographicsocietyparen","ideographicspace",
"ideographicspecialparen","ideographicstockparen","ideographicstudyparen",
"ideographicsunparen","ideographicsuperviseparen","ideographicwaterparen",
"ideographicwoodparen","ideographiczero","ideographmetalcircle",
"ideographmooncircle","ideographnamecircle","ideographsuncircle",
"ideographwatercircle","ideographwoodcircle","ideva","idieresis",
"idieresisacute","idieresiscyrillic","idotbelow","iebrevecyrillic",
"iecyrillic","ieungacirclekorean","ieungaparenkorean","ieungcirclekorean",
"ieungkorean","ieungparenkorean","igrave","igujarati","igurmukhi","ihiragana",
"ihookabove","iibengali","iicyrillic","iideva","iigujarati","iigurmukhi",
"iimatragurmukhi","iinvertedbreve","iishortcyrillic","iivowelsignbengali",
"iivowelsigndeva","iivowelsigngujarati","ij","ikatakana","ikatakanahalfwidth",
"ikorean","ilde","iluyhebrew","imacron","imacroncyrillic",
"imageorapproximatelyequal","imatragurmukhi","imonospace","increment",
"infinity","iniarmenian","integral","integralbottom","integralbt",
"integralex","integraltop","integraltp","intersection","intisquare",
"invbullet","invcircle","invsmileface","iocyrillic","iogonek","iota",
"iotadieresis","iotadieresistonos","iotalatin","iotatonos","iparen",
"irigurmukhi","ismallhiragana","ismallkatakana","ismallkatakanahalfwidth",
"issharbengali","istroke","isuperior","iterationhiragana","iterationkatakana",
"itilde","itildebelow","iubopomofo","iucyrillic","ivowelsignbengali",
"ivowelsigndeva","ivowelsigngujarati","izhitsacyrillic",
"izhitsadblgravecyrillic","j","jaarmenian","jabengali","jadeva","jagujarati",
"jagurmukhi","jbopomofo","jcaron","jcircle","jcircumflex","jcrossedtail",
"jdotlessstroke","jecyrillic","jeemarabic","jeemfinalarabic",
"jeeminitialarabic","jeemmedialarabic","jeharabic","jehfinalarabic",
"jhabengali","jhadeva","jhagujarati","jhagurmukhi","jheharmenian","jis",
"jmonospace","jparen","jsuperior","k","kabashkircyrillic","kabengali",
"kacute","kacyrillic","kadescendercyrillic","kadeva","kaf","kafarabic",
"kafdagesh","kafdageshhebrew","kaffinalarabic","kafhebrew","kafinitialarabic",
"kafmedialarabic","kafrafehebrew","kagujarati","kagurmukhi","kahiragana",
"kahookcyrillic","kakatakana","kakatakanahalfwidth","kappa",
"kappasymbolgreek","kapyeounmieumkorean","kapyeounphieuphkorean",
"kapyeounpieupkorean","kapyeounssangpieupkorean","karoriisquare",
"kashidaautoarabic","kashidaautonosidebearingarabic","kasmallkatakana",
"kasquare","kasraarabic","kasratanarabic","kastrokecyrillic",
"katahiraprolongmarkhalfwidth","kaverticalstrokecyrillic","kbopomofo",
"kcalsquare","kcaron","kcedilla","kcircle","kcommaaccent","kdotbelow",
"keharmenian","kehiragana","kekatakana","kekatakanahalfwidth","kenarmenian",
"kesmallkatakana","kgreenlandic","khabengali","khacyrillic","khadeva",
"khagujarati","khagurmukhi","khaharabic","khahfinalarabic",
"khahinitialarabic","khahmedialarabic","kheicoptic","khhadeva","khhagurmukhi",
"khieukhacirclekorean","khieukhaparenkorean","khieukhcirclekorean",
"khieukhkorean","khieukhparenkorean","khokhaithai","khokhonthai",
"khokhuatthai","khokhwaithai","khomutthai","khook","khorakhangthai",
"khzsquare","kihiragana","kikatakana","kikatakanahalfwidth",
"kiroguramusquare","kiromeetorusquare","kirosquare","kiyeokacirclekorean",
"kiyeokaparenkorean","kiyeokcirclekorean","kiyeokkorean","kiyeokparenkorean",
"kiyeoksioskorean","kjecyrillic","klinebelow","klsquare","kmcubedsquare",
"kmonospace","kmsquaredsquare","kohiragana","kohmsquare","kokaithai",
"kokatakana","kokatakanahalfwidth","kooposquare","koppacyrillic",
"koreanstandardsymbol","koroniscmb","kparen","kpasquare","ksicyrillic",
"ktsquare","kturned","kuhiragana","kukatakana","kukatakanahalfwidth",
"kvsquare","kwsquare","l","labengali","lacute","ladeva","lagujarati",
"lagurmukhi","lakkhangyaothai","lamaleffinalarabic",
"lamalefhamzaabovefinalarabic","lamalefhamzaaboveisolatedarabic",
"lamalefhamzabelowfinalarabic","lamalefhamzabelowisolatedarabic",
"lamalefisolatedarabic","lamalefmaddaabovefinalarabic",
"lamalefmaddaaboveisolatedarabic","lamarabic","lambda","lambdastroke","lamed",
"lameddagesh","lameddageshhebrew","lamedhebrew","lamedholam",
"lamedholamdagesh","lamedholamdageshhebrew","lamedholamhebrew",
"lamfinalarabic","lamhahinitialarabic","laminitialarabic",
"lamjeeminitialarabic","lamkhahinitialarabic","lamlamhehisolatedarabic",
"lammedialarabic","lammeemhahinitialarabic","lammeeminitialarabic",
"lammeemjeeminitialarabic","lammeemkhahinitialarabic","largecircle","lbar",
"lbelt","lbopomofo","lcaron","lcedilla","lcircle","lcircumflexbelow",
"lcommaaccent","ldot","ldotaccent","ldotbelow","ldotbelowmacron",
"leftangleabovecmb","lefttackbelowcmb","less","lessequal",
"lessequalorgreater","lessmonospace","lessorequivalent","lessorgreater",
"lessoverequal","lesssmall","lezh","lfblock","lhookretroflex","lira",
"liwnarmenian","lj","ljecyrillic","ll","lladeva","llagujarati","llinebelow",
"llladeva","llvocalicbengali","llvocalicdeva","llvocalicvowelsignbengali",
"llvocalicvowelsigndeva","lmiddletilde","lmonospace","lmsquare","lochulathai",
"logicaland","logicalnot","logicalnotreversed","logicalor","lolingthai",
"longs","lowlinecenterline","lowlinecmb","lowlinedashed","lozenge","lparen",
"lslash","lsquare","lsuperior","ltshade","luthai","lvocalicbengali",
"lvocalicdeva","lvocalicvowelsignbengali","lvocalicvowelsigndeva","lxsquare",
"m","mabengali","macron","macronbelowcmb","macroncmb","macronlowmod",
"macronmonospace","macute","madeva","magujarati","magurmukhi",
"mahapakhhebrew","mahapakhlefthebrew","mahiragana","maichattawalowleftthai",
"maichattawalowrightthai","maichattawathai","maichattawaupperleftthai",
"maieklowleftthai","maieklowrightthai","maiekthai","maiekupperleftthai",
"maihanakatleftthai","maihanakatthai","maitaikhuleftthai","maitaikhuthai",
"maitholowleftthai","maitholowrightthai","maithothai","maithoupperleftthai",
"maitrilowleftthai","maitrilowrightthai","maitrithai","maitriupperleftthai",
"maiyamokthai","makatakana","makatakanahalfwidth","male","mansyonsquare",
"maqafhebrew","mars","masoracirclehebrew","masquare","mbopomofo","mbsquare",
"mcircle","mcubedsquare","mdotaccent","mdotbelow","meemarabic",
"meemfinalarabic","meeminitialarabic","meemmedialarabic",
"meemmeeminitialarabic","meemmeemisolatedarabic","meetorusquare","mehiragana",
"meizierasquare","mekatakana","mekatakanahalfwidth","mem","memdagesh",
"memdageshhebrew","memhebrew","menarmenian","merkhahebrew",
"merkhakefulahebrew","merkhakefulalefthebrew","merkhalefthebrew","mhook",
"mhzsquare","middledotkatakanahalfwidth","middot","mieumacirclekorean",
"mieumaparenkorean","mieumcirclekorean","mieumkorean","mieumpansioskorean",
"mieumparenkorean","mieumpieupkorean","mieumsioskorean","mihiragana",
"mikatakana","mikatakanahalfwidth","minus","minusbelowcmb","minuscircle",
"minusmod","minusplus","minute","miribaarusquare","mirisquare",
"mlonglegturned","mlsquare","mmcubedsquare","mmonospace","mmsquaredsquare",
"mohiragana","mohmsquare","mokatakana","mokatakanahalfwidth","molsquare",
"momathai","moverssquare","moverssquaredsquare","mparen","mpasquare",
"mssquare","msuperior","mturned","mu","mu1","muasquare","muchgreater",
"muchless","mufsquare","mugreek","mugsquare","muhiragana","mukatakana",
"mukatakanahalfwidth","mulsquare","multiply","mumsquare","munahhebrew",
"munahlefthebrew","musicalnote","musicalnotedbl","musicflatsign",
"musicsharpsign","mussquare","muvsquare","muwsquare","mvmegasquare",
"mvsquare","mwmegasquare","mwsquare","n","nabengali","nabla","nacute",
"nadeva","nagujarati","nagurmukhi","nahiragana","nakatakana",
"nakatakanahalfwidth","napostrophe","nasquare","nbopomofo","nbspace","ncaron",
"ncedilla","ncircle","ncircumflexbelow","ncommaaccent","ndotaccent",
"ndotbelow","nehiragana","nekatakana","nekatakanahalfwidth","newsheqelsign",
"nfsquare","ngabengali","ngadeva","ngagujarati","ngagurmukhi","ngonguthai",
"nhiragana","nhookleft","nhookretroflex","nieunacirclekorean",
"nieunaparenkorean","nieuncieuckorean","nieuncirclekorean","nieunhieuhkorean",
"nieunkorean","nieunpansioskorean","nieunparenkorean","nieunsioskorean",
"nieuntikeutkorean","nihiragana","nikatakana","nikatakanahalfwidth",
"nikhahitleftthai","nikhahitthai","nine","ninearabic","ninebengali",
"ninecircle","ninecircleinversesansserif","ninedeva","ninegujarati",
"ninegurmukhi","ninehackarabic","ninehangzhou","nineideographicparen",
"nineinferior","ninemonospace","nineoldstyle","nineparen","nineperiod",
"ninepersian","nineroman","ninesuperior","nineteencircle","nineteenparen",
"nineteenperiod","ninethai","nj","njecyrillic","nkatakana",
"nkatakanahalfwidth","nlegrightlong","nlinebelow","nmonospace","nmsquare",
"nnabengali","nnadeva","nnagujarati","nnagurmukhi","nnnadeva","nohiragana",
"nokatakana","nokatakanahalfwidth","nonbreakingspace","nonenthai","nonuthai",
"noonarabic","noonfinalarabic","noonghunnaarabic","noonghunnafinalarabic",
"noonhehinitialarabic","nooninitialarabic","noonjeeminitialarabic",
"noonjeemisolatedarabic","noonmedialarabic","noonmeeminitialarabic",
"noonmeemisolatedarabic","noonnoonfinalarabic","notcontains","notelement",
"notelementof","notequal","notgreater","notgreaternorequal",
"notgreaternorless","notidentical","notless","notlessnorequal","notparallel",
"notprecedes","notsubset","notsucceeds","notsuperset","nowarmenian","nparen",
"nssquare","nsuperior","ntilde","nu","nuhiragana","nukatakana",
"nukatakanahalfwidth","nuktabengali","nuktadeva","nuktagujarati",
"nuktagurmukhi","numbersign","numbersignmonospace","numbersignsmall",
"numeralsigngreek","numeralsignlowergreek","numero","nun","nundagesh",
"nundageshhebrew","nunhebrew","nvsquare","nwsquare","nyabengali","nyadeva",
"nyagujarati","nyagurmukhi","o","oacute","oangthai","obarred",
"obarredcyrillic","obarreddieresiscyrillic","obengali","obopomofo","obreve",
"ocandradeva","ocandragujarati","ocandravowelsigndeva",
"ocandravowelsigngujarati","ocaron","ocircle","ocircumflex",
"ocircumflexacute","ocircumflexdotbelow","ocircumflexgrave",
"ocircumflexhookabove","ocircumflextilde","ocyrillic","odblacute","odblgrave",
"odeva","odieresis","odieresiscyrillic","odotbelow","oe","oekorean","ogonek",
"ogonekcmb","ograve","ogujarati","oharmenian","ohiragana","ohookabove",
"ohorn","ohornacute","ohorndotbelow","ohorngrave","ohornhookabove",
"ohorntilde","ohungarumlaut","oi","oinvertedbreve","okatakana",
"okatakanahalfwidth","okorean","olehebrew","omacron","omacronacute",
"omacrongrave","omdeva","omega","omega1","omegacyrillic","omegalatinclosed",
"omegaroundcyrillic","omegatitlocyrillic","omegatonos","omgujarati","omicron",
"omicrontonos","omonospace","one","onearabic","onebengali","onecircle",
"onecircleinversesansserif","onedeva","onedotenleader","oneeighth",
"onefitted","onegujarati","onegurmukhi","onehackarabic","onehalf",
"onehangzhou","oneideographicparen","oneinferior","onemonospace",
"onenumeratorbengali","oneoldstyle","oneparen","oneperiod","onepersian",
"onequarter","oneroman","onesuperior","onethai","onethird","oogonek",
"oogonekmacron","oogurmukhi","oomatragurmukhi","oopen","oparen","openbullet",
"option","ordfeminine","ordmasculine","orthogonal","oshortdeva",
"oshortvowelsigndeva","oslash","oslashacute","osmallhiragana",
"osmallkatakana","osmallkatakanahalfwidth","ostrokeacute","osuperior",
"otcyrillic","otilde","otildeacute","otildedieresis","oubopomofo","overline",
"overlinecenterline","overlinecmb","overlinedashed","overlinedblwavy",
"overlinewavy","overscore","ovowelsignbengali","ovowelsigndeva",
"ovowelsigngujarati","p","paampssquare","paasentosquare","pabengali","pacute",
"padeva","pagedown","pageup","pagujarati","pagurmukhi","pahiragana",
"paiyannoithai","pakatakana","palatalizationcyrilliccmb","palochkacyrillic",
"pansioskorean","paragraph","parallel","parenleft","parenleftaltonearabic",
"parenleftbt","parenleftex","parenleftinferior","parenleftmonospace",
"parenleftsmall","parenleftsuperior","parenlefttp","parenleftvertical",
"parenright","parenrightaltonearabic","parenrightbt","parenrightex",
"parenrightinferior","parenrightmonospace","parenrightsmall",
"parenrightsuperior","parenrighttp","parenrightvertical","partialdiff",
"paseqhebrew","pashtahebrew","pasquare","patah","patah11","patah1d","patah2a",
"patahhebrew","patahnarrowhebrew","patahquarterhebrew","patahwidehebrew",
"pazerhebrew","pbopomofo","pcircle","pdotaccent","pe","pecyrillic","pedagesh",
"pedageshhebrew","peezisquare","pefinaldageshhebrew","peharabic",
"peharmenian","pehebrew","pehfinalarabic","pehinitialarabic","pehiragana",
"pehmedialarabic","pekatakana","pemiddlehookcyrillic","perafehebrew",
"percent","percentarabic","percentmonospace","percentsmall","period",
"periodarmenian","periodcentered","periodhalfwidth","periodinferior",
"periodmonospace","periodsmall","periodsuperior","perispomenigreekcmb",
"perpendicular","perthousand","peseta","pfsquare","phabengali","phadeva",
"phagujarati","phagurmukhi","phi","phi1","phieuphacirclekorean",
"phieuphaparenkorean","phieuphcirclekorean","phieuphkorean",
"phieuphparenkorean","philatin","phinthuthai","phisymbolgreek","phook",
"phophanthai","phophungthai","phosamphaothai","pi","pieupacirclekorean",
"pieupaparenkorean","pieupcieuckorean","pieupcirclekorean",
"pieupkiyeokkorean","pieupkorean","pieupparenkorean","pieupsioskiyeokkorean",
"pieupsioskorean","pieupsiostikeutkorean","pieupthieuthkorean",
"pieuptikeutkorean","pihiragana","pikatakana","pisymbolgreek","piwrarmenian",
"plus","plusbelowcmb","pluscircle","plusminus","plusmod","plusmonospace",
"plussmall","plussuperior","pmonospace","pmsquare","pohiragana",
"pointingindexdownwhite","pointingindexleftwhite","pointingindexrightwhite",
"pointingindexupwhite","pokatakana","poplathai","postalmark","postalmarkface",
"pparen","precedes","prescription","primemod","primereversed","product",
"projective","prolongedkana","propellor","propersubset","propersuperset",
"proportion","proportional","psi","psicyrillic","psilipneumatacyrilliccmb",
"pssquare","puhiragana","pukatakana","pvsquare","pwsquare","q","qadeva",
"qadmahebrew","qafarabic","qaffinalarabic","qafinitialarabic",
"qafmedialarabic","qamats","qamats10","qamats1a","qamats1c","qamats27",
"qamats29","qamats33","qamatsde","qamatshebrew","qamatsnarrowhebrew",
"qamatsqatanhebrew","qamatsqatannarrowhebrew","qamatsqatanquarterhebrew",
"qamatsqatanwidehebrew","qamatsquarterhebrew","qamatswidehebrew",
"qarneyparahebrew","qbopomofo","qcircle","qhook","qmonospace","qof",
"qofdagesh","qofdageshhebrew","qofhatafpatah","qofhatafpatahhebrew",
"qofhatafsegol","qofhatafsegolhebrew","qofhebrew","qofhiriq","qofhiriqhebrew",
"qofholam","qofholamhebrew","qofpatah","qofpatahhebrew","qofqamats",
"qofqamatshebrew","qofqubuts","qofqubutshebrew","qofsegol","qofsegolhebrew",
"qofsheva","qofshevahebrew","qoftsere","qoftserehebrew","qparen",
"quarternote","qubuts","qubuts18","qubuts25","qubuts31","qubutshebrew",
"qubutsnarrowhebrew","qubutsquarterhebrew","qubutswidehebrew","question",
"questionarabic","questionarmenian","questiondown","questiondownsmall",
"questiongreek","questionmonospace","questionsmall","quotedbl","quotedblbase",
"quotedblleft","quotedblmonospace","quotedblprime","quotedblprimereversed",
"quotedblright","quoteleft","quoteleftreversed","quotereversed","quoteright",
"quoterightn","quotesinglbase","quotesingle","quotesinglemonospace","r",
"raarmenian","rabengali","racute","radeva","radical","radicalex",
"radoverssquare","radoverssquaredsquare","radsquare","rafe","rafehebrew",
"ragujarati","ragurmukhi","rahiragana","rakatakana","rakatakanahalfwidth",
"ralowerdiagonalbengali","ramiddlediagonalbengali","ramshorn","ratio",
"rbopomofo","rcaron","rcedilla","rcircle","rcommaaccent","rdblgrave",
"rdotaccent","rdotbelow","rdotbelowmacron","referencemark","reflexsubset",
"reflexsuperset","registered","registersans","registerserif","reharabic",
"reharmenian","rehfinalarabic","rehiragana","rehyehaleflamarabic",
"rekatakana","rekatakanahalfwidth","resh","reshdageshhebrew","reshhatafpatah",
"reshhatafpatahhebrew","reshhatafsegol","reshhatafsegolhebrew","reshhebrew",
"reshhiriq","reshhiriqhebrew","reshholam","reshholamhebrew","reshpatah",
"reshpatahhebrew","reshqamats","reshqamatshebrew","reshqubuts",
"reshqubutshebrew","reshsegol","reshsegolhebrew","reshsheva",
"reshshevahebrew","reshtsere","reshtserehebrew","reversedtilde","reviahebrew",
"reviamugrashhebrew","revlogicalnot","rfishhook","rfishhookreversed",
"rhabengali","rhadeva","rho","rhook","rhookturned","rhookturnedsuperior",
"rhosymbolgreek","rhotichookmod","rieulacirclekorean","rieulaparenkorean",
"rieulcirclekorean","rieulhieuhkorean","rieulkiyeokkorean",
"rieulkiyeoksioskorean","rieulkorean","rieulmieumkorean","rieulpansioskorean",
"rieulparenkorean","rieulphieuphkorean","rieulpieupkorean",
"rieulpieupsioskorean","rieulsioskorean","rieulthieuthkorean",
"rieultikeutkorean","rieulyeorinhieuhkorean","rightangle","righttackbelowcmb",
"righttriangle","rihiragana","rikatakana","rikatakanahalfwidth","ring",
"ringbelowcmb","ringcmb","ringhalfleft","ringhalfleftarmenian",
"ringhalfleftbelowcmb","ringhalfleftcentered","ringhalfright",
"ringhalfrightbelowcmb","ringhalfrightcentered","rinvertedbreve",
"rittorusquare","rlinebelow","rlongleg","rlonglegturned","rmonospace",
"rohiragana","rokatakana","rokatakanahalfwidth","roruathai","rparen",
"rrabengali","rradeva","rragurmukhi","rreharabic","rrehfinalarabic",
"rrvocalicbengali","rrvocalicdeva","rrvocalicgujarati",
"rrvocalicvowelsignbengali","rrvocalicvowelsigndeva",
"rrvocalicvowelsigngujarati","rsuperior","rtblock","rturned",
"rturnedsuperior","ruhiragana","rukatakana","rukatakanahalfwidth",
"rupeemarkbengali","rupeesignbengali","rupiah","ruthai","rvocalicbengali",
"rvocalicdeva","rvocalicgujarati","rvocalicvowelsignbengali",
"rvocalicvowelsigndeva","rvocalicvowelsigngujarati","s","sabengali","sacute",
"sacutedotaccent","sadarabic","sadeva","sadfinalarabic","sadinitialarabic",
"sadmedialarabic","sagujarati","sagurmukhi","sahiragana","sakatakana",
"sakatakanahalfwidth","sallallahoualayhewasallamarabic","samekh",
"samekhdagesh","samekhdageshhebrew","samekhhebrew","saraaathai","saraaethai",
"saraaimaimalaithai","saraaimaimuanthai","saraamthai","saraathai","saraethai",
"saraiileftthai","saraiithai","saraileftthai","saraithai","saraothai",
"saraueeleftthai","saraueethai","saraueleftthai","sarauethai","sarauthai",
"sarauuthai","sbopomofo","scaron","scarondotaccent","scedilla","schwa",
"schwacyrillic","schwadieresiscyrillic","schwahook","scircle","scircumflex",
"scommaaccent","sdotaccent","sdotbelow","sdotbelowdotaccent",
"seagullbelowcmb","second","secondtonechinese","section","seenarabic",
"seenfinalarabic","seeninitialarabic","seenmedialarabic","segol","segol13",
"segol1f","segol2c","segolhebrew","segolnarrowhebrew","segolquarterhebrew",
"segoltahebrew","segolwidehebrew","seharmenian","sehiragana","sekatakana",
"sekatakanahalfwidth","semicolon","semicolonarabic","semicolonmonospace",
"semicolonsmall","semivoicedmarkkana","semivoicedmarkkanahalfwidth",
"sentisquare","sentosquare","seven","sevenarabic","sevenbengali",
"sevencircle","sevencircleinversesansserif","sevendeva","seveneighths",
"sevengujarati","sevengurmukhi","sevenhackarabic","sevenhangzhou",
"sevenideographicparen","seveninferior","sevenmonospace","sevenoldstyle",
"sevenparen","sevenperiod","sevenpersian","sevenroman","sevensuperior",
"seventeencircle","seventeenparen","seventeenperiod","seventhai","sfthyphen",
"shaarmenian","shabengali","shacyrillic","shaddaarabic","shaddadammaarabic",
"shaddadammatanarabic","shaddafathaarabic","shaddafathatanarabic",
"shaddakasraarabic","shaddakasratanarabic","shade","shadedark","shadelight",
"shademedium","shadeva","shagujarati","shagurmukhi","shalshelethebrew",
"shbopomofo","shchacyrillic","sheenarabic","sheenfinalarabic",
"sheeninitialarabic","sheenmedialarabic","sheicoptic","sheqel","sheqelhebrew",
"sheva","sheva115","sheva15","sheva22","sheva2e","shevahebrew",
"shevanarrowhebrew","shevaquarterhebrew","shevawidehebrew","shhacyrillic",
"shimacoptic","shin","shindagesh","shindageshhebrew","shindageshshindot",
"shindageshshindothebrew","shindageshsindot","shindageshsindothebrew",
"shindothebrew","shinhebrew","shinshindot","shinshindothebrew","shinsindot",
"shinsindothebrew","shook","sigma","sigma1","sigmafinal",
"sigmalunatesymbolgreek","sihiragana","sikatakana","sikatakanahalfwidth",
"siluqhebrew","siluqlefthebrew","similar","sindothebrew","siosacirclekorean",
"siosaparenkorean","sioscieuckorean","sioscirclekorean","sioskiyeokkorean",
"sioskorean","siosnieunkorean","siosparenkorean","siospieupkorean",
"siostikeutkorean","six","sixarabic","sixbengali","sixcircle",
"sixcircleinversesansserif","sixdeva","sixgujarati","sixgurmukhi",
"sixhackarabic","sixhangzhou","sixideographicparen","sixinferior",
"sixmonospace","sixoldstyle","sixparen","sixperiod","sixpersian","sixroman",
"sixsuperior","sixteencircle","sixteencurrencydenominatorbengali",
"sixteenparen","sixteenperiod","sixthai","slash","slashmonospace","slong",
"slongdotaccent","smileface","smonospace","sofpasuqhebrew","softhyphen",
"softsigncyrillic","sohiragana","sokatakana","sokatakanahalfwidth",
"soliduslongoverlaycmb","solidusshortoverlaycmb","sorusithai","sosalathai",
"sosothai","sosuathai","space","spacehackarabic","spade","spadesuitblack",
"spadesuitwhite","sparen","squarebelowcmb","squarecc","squarecm",
"squarediagonalcrosshatchfill","squarehorizontalfill","squarekg","squarekm",
"squarekmcapital","squareln","squarelog","squaremg","squaremil","squaremm",
"squaremsquared","squareorthogonalcrosshatchfill",
"squareupperlefttolowerrightfill","squareupperrighttolowerleftfill",
"squareverticalfill","squarewhitewithsmallblack","srsquare","ssabengali",
"ssadeva","ssagujarati","ssangcieuckorean","ssanghieuhkorean",
"ssangieungkorean","ssangkiyeokkorean","ssangnieunkorean","ssangpieupkorean",
"ssangsioskorean","ssangtikeutkorean","ssuperior","sterling",
"sterlingmonospace","strokelongoverlaycmb","strokeshortoverlaycmb","subset",
"subsetnotequal","subsetorequal","succeeds","suchthat","suhiragana",
"sukatakana","sukatakanahalfwidth","sukunarabic","summation","sun","superset",
"supersetnotequal","supersetorequal","svsquare","syouwaerasquare","t",
"tabengali","tackdown","tackleft","tadeva","tagujarati","tagurmukhi",
"taharabic","tahfinalarabic","tahinitialarabic","tahiragana",
"tahmedialarabic","taisyouerasquare","takatakana","takatakanahalfwidth",
"tatweelarabic","tau","tav","tavdages","tavdagesh","tavdageshhebrew",
"tavhebrew","tbar","tbopomofo","tcaron","tccurl","tcedilla","tcheharabic",
"tchehfinalarabic","tchehinitialarabic","tchehmedialarabic",
"tchehmeeminitialarabic","tcircle","tcircumflexbelow","tcommaaccent",
"tdieresis","tdotaccent","tdotbelow","tecyrillic","tedescendercyrillic",
"teharabic","tehfinalarabic","tehhahinitialarabic","tehhahisolatedarabic",
"tehinitialarabic","tehiragana","tehjeeminitialarabic",
"tehjeemisolatedarabic","tehmarbutaarabic","tehmarbutafinalarabic",
"tehmedialarabic","tehmeeminitialarabic","tehmeemisolatedarabic",
"tehnoonfinalarabic","tekatakana","tekatakanahalfwidth","telephone",
"telephoneblack","telishagedolahebrew","telishaqetanahebrew","tencircle",
"tenideographicparen","tenparen","tenperiod","tenroman","tesh","tet",
"tetdagesh","tetdageshhebrew","tethebrew","tetsecyrillic","tevirhebrew",
"tevirlefthebrew","thabengali","thadeva","thagujarati","thagurmukhi",
"thalarabic","thalfinalarabic","thanthakhatlowleftthai",
"thanthakhatlowrightthai","thanthakhatthai","thanthakhatupperleftthai",
"theharabic","thehfinalarabic","thehinitialarabic","thehmedialarabic",
"thereexists","therefore","theta","theta1","thetasymbolgreek",
"thieuthacirclekorean","thieuthaparenkorean","thieuthcirclekorean",
"thieuthkorean","thieuthparenkorean","thirteencircle","thirteenparen",
"thirteenperiod","thonangmonthothai","thook","thophuthaothai","thorn",
"thothahanthai","thothanthai","thothongthai","thothungthai",
"thousandcyrillic","thousandsseparatorarabic","thousandsseparatorpersian",
"three","threearabic","threebengali","threecircle",
"threecircleinversesansserif","threedeva","threeeighths","threegujarati",
"threegurmukhi","threehackarabic","threehangzhou","threeideographicparen",
"threeinferior","threemonospace","threenumeratorbengali","threeoldstyle",
"threeparen","threeperiod","threepersian","threequarters",
"threequartersemdash","threeroman","threesuperior","threethai","thzsquare",
"tihiragana","tikatakana","tikatakanahalfwidth","tikeutacirclekorean",
"tikeutaparenkorean","tikeutcirclekorean","tikeutkorean","tikeutparenkorean",
"tilde","tildebelowcmb","tildecmb","tildecomb","tildedoublecmb",
"tildeoperator","tildeoverlaycmb","tildeverticalcmb","timescircle",
"tipehahebrew","tipehalefthebrew","tippigurmukhi","titlocyrilliccmb",
"tiwnarmenian","tlinebelow","tmonospace","toarmenian","tohiragana",
"tokatakana","tokatakanahalfwidth","tonebarextrahighmod","tonebarextralowmod",
"tonebarhighmod","tonebarlowmod","tonebarmidmod","tonefive","tonesix",
"tonetwo","tonos","tonsquare","topatakthai","tortoiseshellbracketleft",
"tortoiseshellbracketleftsmall","tortoiseshellbracketleftvertical",
"tortoiseshellbracketright","tortoiseshellbracketrightsmall",
"tortoiseshellbracketrightvertical","totaothai","tpalatalhook","tparen",
"trademark","trademarksans","trademarkserif","tretroflexhook","triagdn",
"triaglf","triagrt","triagup","ts","tsadi","tsadidagesh","tsadidageshhebrew",
"tsadihebrew","tsecyrillic","tsere","tsere12","tsere1e","tsere2b",
"tserehebrew","tserenarrowhebrew","tserequarterhebrew","tserewidehebrew",
"tshecyrillic","tsuperior","ttabengali","ttadeva","ttagujarati","ttagurmukhi",
"tteharabic","ttehfinalarabic","ttehinitialarabic","ttehmedialarabic",
"tthabengali","tthadeva","tthagujarati","tthagurmukhi","tturned","tuhiragana",
"tukatakana","tukatakanahalfwidth","tusmallhiragana","tusmallkatakana",
"tusmallkatakanahalfwidth","twelvecircle","twelveparen","twelveperiod",
"twelveroman","twentycircle","twentyhangzhou","twentyparen","twentyperiod",
"two","twoarabic","twobengali","twocircle","twocircleinversesansserif",
"twodeva","twodotenleader","twodotleader","twodotleadervertical",
"twogujarati","twogurmukhi","twohackarabic","twohangzhou",
"twoideographicparen","twoinferior","twomonospace","twonumeratorbengali",
"twooldstyle","twoparen","twoperiod","twopersian","tworoman","twostroke",
"twosuperior","twothai","twothirds","u","uacute","ubar","ubengali",
"ubopomofo","ubreve","ucaron","ucircle","ucircumflex","ucircumflexbelow",
"ucyrillic","udattadeva","udblacute","udblgrave","udeva","udieresis",
"udieresisacute","udieresisbelow","udieresiscaron","udieresiscyrillic",
"udieresisgrave","udieresismacron","udotbelow","ugrave","ugujarati",
"ugurmukhi","uhiragana","uhookabove","uhorn","uhornacute","uhorndotbelow",
"uhorngrave","uhornhookabove","uhorntilde","uhungarumlaut",
"uhungarumlautcyrillic","uinvertedbreve","ukatakana","ukatakanahalfwidth",
"ukcyrillic","ukorean","umacron","umacroncyrillic","umacrondieresis",
"umatragurmukhi","umonospace","underscore","underscoredbl",
"underscoremonospace","underscorevertical","underscorewavy","union",
"universal","uogonek","uparen","upblock","upperdothebrew","upsilon",
"upsilondieresis","upsilondieresistonos","upsilonlatin","upsilontonos",
"uptackbelowcmb","uptackmod","uragurmukhi","uring","ushortcyrillic",
"usmallhiragana","usmallkatakana","usmallkatakanahalfwidth",
"ustraightcyrillic","ustraightstrokecyrillic","utilde","utildeacute",
"utildebelow","uubengali","uudeva","uugujarati","uugurmukhi",
"uumatragurmukhi","uuvowelsignbengali","uuvowelsigndeva",
"uuvowelsigngujarati","uvowelsignbengali","uvowelsigndeva",
"uvowelsigngujarati","v","vadeva","vagujarati","vagurmukhi","vakatakana",
"vav","vavdagesh","vavdagesh65","vavdageshhebrew","vavhebrew","vavholam",
"vavholamhebrew","vavvavhebrew","vavyodhebrew","vcircle","vdotbelow",
"vecyrillic","veharabic","vehfinalarabic","vehinitialarabic",
"vehmedialarabic","vekatakana","venus","verticalbar","verticallineabovecmb",
"verticallinebelowcmb","verticallinelowmod","verticallinemod","vewarmenian",
"vhook","vikatakana","viramabengali","viramadeva","viramagujarati",
"visargabengali","visargadeva","visargagujarati","vmonospace","voarmenian",
"voicediterationhiragana","voicediterationkatakana","voicedmarkkana",
"voicedmarkkanahalfwidth","vokatakana","vparen","vtilde","vturned",
"vuhiragana","vukatakana","w","wacute","waekorean","wahiragana","wakatakana",
"wakatakanahalfwidth","wakorean","wasmallhiragana","wasmallkatakana",
"wattosquare","wavedash","wavyunderscorevertical","wawarabic",
"wawfinalarabic","wawhamzaabovearabic","wawhamzaabovefinalarabic","wbsquare",
"wcircle","wcircumflex","wdieresis","wdotaccent","wdotbelow","wehiragana",
"weierstrass","wekatakana","wekorean","weokorean","wgrave","whitebullet",
"whitecircle","whitecircleinverse","whitecornerbracketleft",
"whitecornerbracketleftvertical","whitecornerbracketright",
"whitecornerbracketrightvertical","whitediamond",
"whitediamondcontainingblacksmalldiamond","whitedownpointingsmalltriangle",
"whitedownpointingtriangle","whiteleftpointingsmalltriangle",
"whiteleftpointingtriangle","whitelenticularbracketleft",
"whitelenticularbracketright","whiterightpointingsmalltriangle",
"whiterightpointingtriangle","whitesmallsquare","whitesmilingface",
"whitesquare","whitestar","whitetelephone","whitetortoiseshellbracketleft",
"whitetortoiseshellbracketright","whiteuppointingsmalltriangle",
"whiteuppointingtriangle","wihiragana","wikatakana","wikorean","wmonospace",
"wohiragana","wokatakana","wokatakanahalfwidth","won","wonmonospace",
"wowaenthai","wparen","wring","wsuperior","wturned","wynn","x","xabovecmb",
"xbopomofo","xcircle","xdieresis","xdotaccent","xeharmenian","xi",
"xmonospace","xparen","xsuperior","y","yaadosquare","yabengali","yacute",
"yadeva","yaekorean","yagujarati","yagurmukhi","yahiragana","yakatakana",
"yakatakanahalfwidth","yakorean","yamakkanthai","yasmallhiragana",
"yasmallkatakana","yasmallkatakanahalfwidth","yatcyrillic","ycircle",
"ycircumflex","ydieresis","ydotaccent","ydotbelow","yeharabic",
"yehbarreearabic","yehbarreefinalarabic","yehfinalarabic",
"yehhamzaabovearabic","yehhamzaabovefinalarabic","yehhamzaaboveinitialarabic",
"yehhamzaabovemedialarabic","yehinitialarabic","yehmedialarabic",
"yehmeeminitialarabic","yehmeemisolatedarabic","yehnoonfinalarabic",
"yehthreedotsbelowarabic","yekorean","yen","yenmonospace","yeokorean",
"yeorinhieuhkorean","yerahbenyomohebrew","yerahbenyomolefthebrew",
"yericyrillic","yerudieresiscyrillic","yesieungkorean",
"yesieungpansioskorean","yesieungsioskorean","yetivhebrew","ygrave","yhook",
"yhookabove","yiarmenian","yicyrillic","yikorean","yinyang","yiwnarmenian",
"ymonospace","yod","yoddagesh","yoddageshhebrew","yodhebrew","yodyodhebrew",
"yodyodpatahhebrew","yohiragana","yoikorean","yokatakana",
"yokatakanahalfwidth","yokorean","yosmallhiragana","yosmallkatakana",
"yosmallkatakanahalfwidth","yotgreek","yoyaekorean","yoyakorean","yoyakthai",
"yoyingthai","yparen","ypogegrammeni","ypogegrammenigreekcmb","yr","yring",
"ysuperior","ytilde","yturned","yuhiragana","yuikorean","yukatakana",
"yukatakanahalfwidth","yukorean","yusbigcyrillic","yusbigiotifiedcyrillic",
"yuslittlecyrillic","yuslittleiotifiedcyrillic","yusmallhiragana",
"yusmallkatakana","yusmallkatakanahalfwidth","yuyekorean","yuyeokorean",
"yyabengali","yyadeva","z","zaarmenian","zacute","zadeva","zagurmukhi",
"zaharabic","zahfinalarabic","zahinitialarabic","zahiragana",
"zahmedialarabic","zainarabic","zainfinalarabic","zakatakana",
"zaqefgadolhebrew","zaqefqatanhebrew","zarqahebrew","zayin","zayindagesh",
"zayindageshhebrew","zayinhebrew","zbopomofo","zcaron","zcircle",
"zcircumflex","zcurl","zdot","zdotaccent","zdotbelow","zecyrillic",
"zedescendercyrillic","zedieresiscyrillic","zehiragana","zekatakana","zero",
"zeroarabic","zerobengali","zerodeva","zerogujarati","zerogurmukhi",
"zerohackarabic","zeroinferior","zeromonospace","zerooldstyle","zeropersian",
"zerosuperior","zerothai","zerowidthjoiner","zerowidthnonjoiner",
"zerowidthspace","zeta","zhbopomofo","zhearmenian","zhebrevecyrillic",
"zhecyrillic","zhedescendercyrillic","zhedieresiscyrillic","zihiragana",
"zikatakana","zinorhebrew","zlinebelow","zmonospace","zohiragana",
"zokatakana","zparen","zretroflexhook","zstroke","zuhiragana","zukatakana",
};
 
static const unsigned short agl_code_list[] = {
65,198,508,482,63462,193,63457,258,7854,1232,7862,7856,7858,7860,461,9398,194,
7844,7852,7846,7848,63458,7850,63177,63412,1040,512,196,1234,478,63460,7840,
480,192,63456,7842,1236,514,913,902,256,65313,260,197,506,7680,63461,63329,
195,63459,1329,66,9399,7682,7684,1041,1330,914,385,7686,65314,63220,63330,386,
67,1342,262,63178,63221,268,199,7688,63463,9400,264,266,266,63416,1353,1212,
1063,1214,1206,1268,1347,1227,1208,935,391,63222,65315,1361,63331,68,497,452,
1332,393,270,7696,9401,7698,272,7690,7692,1044,1006,8710,916,394,63179,63180,
63181,63400,988,1026,7694,65316,63223,272,63332,395,498,453,1248,1029,1039,69,
201,63465,276,282,7708,1333,9402,202,7870,7704,7878,7872,7874,63466,7876,1028,
516,203,63467,278,278,7864,1060,200,63464,1335,7866,8551,518,1124,1051,8554,
274,7702,7700,1052,65317,1053,1186,330,1188,1223,280,400,917,904,1056,398,
1069,1057,1194,425,63333,919,1336,905,208,63472,7868,7706,8364,439,494,440,70,
9403,7710,1366,996,401,1138,8548,65318,8547,63334,71,13191,500,915,404,1002,
286,486,290,9404,284,290,288,288,1043,1346,1172,1170,1168,403,1331,1027,7712,
65319,63182,63328,63335,667,484,72,9679,9642,9643,9633,13259,1192,1202,1066,
294,7722,7720,9405,292,7718,7714,7716,65320,1344,1000,63336,63183,63224,13200,
73,1071,306,1070,205,63469,300,463,9406,206,63470,1030,520,207,7726,1252,
63471,304,304,7882,1238,1045,8465,204,63468,7880,1048,522,1049,298,1250,65321,
1339,1025,302,921,406,938,906,63337,407,296,7724,1140,1142,74,1345,9407,308,
1032,1355,65322,63338,75,13189,13261,1184,7728,1050,1178,1219,922,1182,1180,
488,310,9408,310,7730,1364,1343,1061,998,408,1036,7732,65323,1152,990,1134,
63339,76,455,63167,313,923,317,315,9409,7740,315,319,319,7734,7736,1340,456,
1033,7738,65324,321,63225,63340,77,13190,63184,63407,7742,9410,7744,7746,1348,
65325,63341,412,924,78,458,323,327,325,9411,7754,325,7748,7750,413,8552,459,
1034,7752,65326,1350,63342,209,63473,925,79,338,63226,211,63475,1256,1258,334,
465,415,9412,212,7888,7896,7890,7892,63476,7894,1054,336,524,214,1254,63478,
7884,63227,210,63474,1365,8486,7886,416,7898,7906,7900,7902,7904,336,418,526,
332,7762,7760,8486,1120,937,1146,1148,911,927,908,65327,8544,490,492,390,216,
510,63480,63343,510,1150,213,7756,7758,63477,80,7764,9413,7766,1055,1354,1190,
934,420,928,1363,65328,936,1136,63344,81,9414,65329,63345,82,1356,340,344,342,
9415,342,528,7768,7770,7772,1360,8476,929,63228,530,7774,65330,63346,641,694,
83,9484,9492,9488,9496,9532,9516,9524,9500,9508,9472,9474,9569,9570,9558,9557,
9571,9553,9559,9565,9564,9563,9566,9567,9562,9556,9577,9574,9568,9552,9580,
9575,9576,9572,9573,9561,9560,9554,9555,9579,9578,346,7780,992,352,7782,63229,
350,399,1240,1242,9416,348,536,7776,7778,7784,1357,8550,1351,1064,1065,994,
1210,1004,931,8549,65331,1068,63347,986,84,932,358,356,354,9417,7792,354,7786,
7788,1058,1196,8553,1204,920,428,222,63486,8546,63230,1359,7790,65332,1337,
444,388,423,430,1062,1035,63348,8555,8545,85,218,63482,364,467,9418,219,7798,
63483,1059,368,532,220,471,7794,473,1264,475,469,63484,7908,217,63481,7910,
431,7912,7920,7914,7916,7918,368,1266,534,1144,362,1262,7802,65333,370,933,
978,979,433,939,980,978,910,366,1038,63349,1198,1200,360,7800,7796,86,9419,
7806,1042,1358,434,65334,1352,63350,7804,87,7810,9420,372,7812,7814,7816,7808,
65335,63351,88,9421,7820,7818,1341,926,65336,63352,89,221,63485,1122,9422,374,
376,63487,7822,7924,1067,1272,7922,435,7926,1349,1031,1362,65337,63353,7928,
1130,1132,1126,1128,90,1334,377,381,63231,9423,7824,379,379,7826,1047,1176,
1246,918,1338,1217,1046,1174,1244,7828,65338,63354,437,97,2438,225,2310,2694,
2566,2622,13059,2494,2366,2750,1375,2416,2437,12570,259,7855,1233,7863,7857,
7859,7861,462,9424,226,7845,7853,7847,7849,7851,180,791,769,769,2388,719,833,
1072,513,2673,2309,228,1235,479,7841,481,230,509,12624,483,8213,8356,1040,
1041,1042,1043,1044,1045,1025,1046,1047,1048,1049,1050,1051,1052,1053,1054,
1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,
1070,1071,1168,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1038,
63172,63173,1072,1073,1074,1075,1076,1077,1105,1078,1079,1080,1081,1082,1083,
1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,
1099,1100,1101,1102,1103,1169,1106,1107,1108,1109,1110,1111,1112,1113,1114,
1115,1116,1118,1039,1122,1138,1140,63174,1119,1123,1139,1141,63175,63176,1241,
8206,8207,8205,1642,1548,1632,1633,1634,1635,1636,1637,1638,1639,1640,1641,
1563,1567,1569,1570,1571,1572,1573,1574,1575,1576,1577,1578,1579,1580,1581,
1582,1583,1584,1585,1586,1587,1588,1589,1590,1591,1592,1593,1594,1600,1601,
1602,1603,1604,1605,1606,1608,1609,1610,1611,1612,1613,1614,1615,1616,1617,
1618,1607,1700,1662,1670,1688,1711,1657,1672,1681,1722,1746,1749,8362,1470,
1475,1488,1489,1490,1491,1492,1493,1494,1495,1496,1497,1498,1499,1500,1501,
1502,1503,1504,1505,1506,1507,1508,1509,1510,1511,1512,1513,1514,64298,64299,
64331,64287,1520,1521,1522,64309,1460,1461,1462,1467,1464,1463,1456,1458,1457,
1459,1474,1473,1465,1468,1469,1471,1472,700,8453,8467,8470,8236,8237,8238,
8204,1645,701,224,2693,2565,12354,7843,2448,12574,2320,1237,2704,2576,2632,
1593,65226,65227,65228,515,2504,2376,2760,12450,65393,12623,1488,1575,64304,
65166,1571,65156,1573,65160,1488,64335,1570,65154,1609,65264,65267,65268,
64302,64303,8501,8780,945,940,257,65345,38,65286,63270,13250,12578,12580,3674,
8736,12296,65087,12297,65088,9001,9002,8491,903,2386,2434,2306,2690,261,13056,
9372,1370,700,63743,8784,8776,8786,8773,12686,12685,8978,7834,229,507,7681,
8596,8675,8672,8674,8673,8660,8659,8656,8658,8657,8595,8601,8600,8681,709,706,
707,708,63719,8592,8656,8653,8646,8678,8594,8655,10142,8644,8680,8676,8677,
8593,8597,8616,8616,8598,8645,8599,8679,63718,94,65342,126,65374,593,594,
12353,12449,65383,42,1645,1645,8727,65290,65121,8258,63209,8771,64,227,65312,
65131,592,2452,12576,2324,2708,2580,2519,2636,2508,2380,2764,2365,1377,1506,
64288,1506,98,2476,92,65340,2348,2732,2604,12400,3647,12496,124,65372,12549,
9425,7683,7685,9836,8757,1073,1576,65168,65169,12409,65170,64671,64520,64621,
12505,1378,1489,946,976,64305,64305,1489,64332,2477,2349,2733,2605,595,12403,
12499,664,2562,13105,9679,9670,9660,9668,9664,12304,65083,12305,65084,9699,
9698,9644,9658,9654,9642,9787,9632,9733,9700,9701,9652,9650,9251,7687,9608,
65346,3610,12412,12508,9373,13251,63732,123,63731,63730,65371,65115,63729,
65079,125,63742,63741,65373,65116,63740,65080,91,63728,63727,65339,63726,93,
63739,63738,65341,63737,728,814,774,815,785,865,810,826,166,384,63210,387,
12406,12502,8226,9688,8729,9678,99,1390,2458,263,2330,2714,2586,13192,2433,
784,2305,2689,8682,8453,711,812,780,8629,12568,269,231,7689,9426,265,597,267,
267,13253,184,807,162,8451,63199,65504,63394,63200,1401,2459,2331,2715,2587,
12564,1213,10003,1095,1215,1207,1269,1395,1228,1209,967,12919,12823,12905,
12618,12809,3594,3592,3593,3596,392,12918,12822,12904,12616,12808,12828,9675,
8855,8857,8853,12342,9680,9681,710,813,770,8999,450,448,449,451,9827,9827,
9831,13220,65347,13216,1409,58,8353,65306,8353,65109,721,720,44,787,789,63171,
1548,1373,63201,65292,788,701,65104,63202,786,699,9788,8773,8750,8963,6,7,8,
24,13,17,18,19,20,127,16,25,5,4,27,23,3,12,28,29,9,10,21,30,15,14,2,1,26,22,
31,11,169,63721,63193,12300,65378,65089,12301,65379,65090,13183,13255,13254,
9374,8354,663,8911,8910,164,63185,63186,63188,63189,100,1380,2470,1590,2342,
65214,65215,65216,1468,1468,8224,8225,2726,2598,12384,12480,1583,1491,64307,
64307,1491,1491,1491,1491,1491,1491,1491,1491,1491,1491,1491,1491,1491,1491,
1491,1491,1491,1491,1491,1491,1491,65194,1615,1615,1612,1612,2404,1447,1447,
1157,63187,12298,65085,12299,65086,811,8660,8658,2405,63190,783,8748,8215,819,
831,698,8214,782,12553,13256,271,7697,9427,7699,273,2465,2337,2721,2593,1672,
64393,2396,2466,2338,2722,2594,7691,7693,1643,1643,1076,176,1453,12391,1007,
12487,9003,8998,948,397,2552,676,2471,2343,2727,2599,599,901,836,9830,9826,
168,63191,804,776,63192,901,12386,12482,12291,247,8739,8725,1106,9619,7695,
13207,273,65348,9604,3598,3604,12393,12489,36,63203,65284,63268,65129,63204,
8363,13094,729,775,803,803,12539,305,63166,644,8901,9676,64287,64287,798,725,
9375,63211,598,396,12389,12485,499,675,454,677,1249,1109,1119,101,233,9793,
2447,12572,277,2317,2701,2373,2757,283,7709,1381,1415,9428,234,7871,7705,7879,
7873,7875,7877,1108,517,2319,235,279,279,7865,2575,2631,1092,232,2703,1383,
12573,12360,7867,12575,56,1640,2542,9319,10129,2414,9329,9349,9369,2798,2670,
1640,12328,9835,12839,8328,65304,63288,9339,9359,1784,8567,8312,3672,519,1125,
12456,65396,2676,12628,1083,8712,9322,9342,9362,8570,8230,8942,275,7703,7701,
1084,8212,65073,65349,1371,8709,12579,1085,8211,65074,1187,331,12581,1189,
1224,8194,281,12627,603,666,604,606,605,9376,949,941,61,65309,65126,8316,8801,
12582,1088,600,1101,1089,1195,643,646,2318,2374,426,645,12359,12455,65386,
8494,63212,951,1384,942,240,7869,7707,1425,1425,1425,1425,477,12641,8364,2503,
2375,2759,33,1372,8252,161,63393,65281,63265,8707,658,495,659,441,442,102,
2398,2654,8457,1614,1614,1611,12552,9429,7711,1601,1414,65234,65235,65236,997,
9792,64256,64259,64260,64257,9326,9346,9366,8210,9632,9644,1498,64314,64314,
1498,1498,1498,1498,1498,1501,1501,1503,1503,1507,1507,1509,1509,713,9673,
1139,53,1637,2539,9316,10126,2411,8541,2795,2667,1637,12325,12836,8325,65301,
63285,9336,9356,1781,8564,8309,3669,64258,402,65350,13209,3615,3613,3663,8704,
52,1636,2538,9315,10125,2410,2794,2666,1636,12324,12835,8324,65300,2551,63284,
9335,9355,1780,8563,8308,9325,9345,9365,3668,715,9377,8260,8355,103,2455,501,
2327,1711,64403,64404,64405,2711,2583,12364,12460,947,611,736,1003,12557,287,
487,291,9430,285,291,289,289,1075,12370,12466,8785,1436,1523,1437,223,1438,
1524,12307,2456,1394,2328,2712,2584,1594,65230,65231,65232,1173,1171,1169,
2394,2650,608,13203,12366,12462,1379,1490,64306,64306,1490,1107,446,660,662,
704,661,705,740,673,674,7713,65351,12372,12468,9378,13228,8711,96,790,768,768,
2387,718,65344,832,62,8805,8923,65310,8819,8823,8807,65125,609,485,12368,171,
187,8249,8250,12464,13080,13257,104,1193,1729,2489,1203,2361,2745,2617,1581,
65186,65187,12399,65188,13098,12495,65418,2637,1569,1569,1569,1569,1569,1569,
1569,1569,1569,12644,1098,8636,8640,13258,1458,1458,1458,1458,1458,1458,1458,
1458,1459,1459,1459,1459,1459,1459,1459,1459,1457,1457,1457,1457,1457,1457,
1457,1457,295,12559,7723,7721,9431,293,7719,7715,7717,1492,9829,9829,9825,
64308,64308,1729,1607,1492,64423,65258,65258,64421,64420,64424,65259,12408,
64425,65260,13179,12504,65421,13110,615,13113,1495,1495,614,689,12923,12827,
12909,12622,12813,12402,12498,65419,1460,1460,1460,1460,1460,1460,1460,1460,
7830,65352,1392,3627,12411,12507,65422,1465,1465,1465,1465,1465,1465,1465,
1465,3630,777,777,801,802,13122,1001,8213,795,9832,8962,9379,688,613,12405,
13107,12501,65420,733,779,405,45,63205,65293,65123,63206,8208,105,237,1103,
2439,12583,301,464,9432,238,1110,521,12943,12939,12863,12858,12965,12294,
12289,65380,12855,12963,12847,12861,12957,12864,12950,12854,12843,12850,12964,
12293,12952,12856,12967,12966,12969,12846,12842,12852,12290,12958,12867,12857,
12862,12968,12953,12866,12851,12288,12853,12849,12859,12848,12860,12844,12845,
12295,12942,12938,12948,12944,12940,12941,2311,239,7727,1253,7883,1239,1077,
12917,12821,12903,12615,12807,236,2695,2567,12356,7881,2440,1080,2312,2696,
2568,2624,523,1081,2496,2368,2752,307,12452,65394,12643,732,1452,299,1251,
8787,2623,65353,8710,8734,1387,8747,8993,8993,63733,8992,8992,8745,13061,9688,
9689,9787,1105,303,953,970,912,617,943,9380,2674,12355,12451,65384,2554,616,
63213,12445,12541,297,7725,12585,1102,2495,2367,2751,1141,1143,106,1393,2460,
2332,2716,2588,12560,496,9433,309,669,607,1112,1580,65182,65183,65184,1688,
64395,2461,2333,2717,2589,1403,12292,65354,9381,690,107,1185,2453,7729,1082,
1179,2325,1499,1603,64315,64315,65242,1499,65243,65244,64333,2709,2581,12363,
1220,12459,65398,954,1008,12657,12676,12664,12665,13069,1600,1600,12533,13188,
1616,1613,1183,65392,1181,12558,13193,489,311,9434,311,7731,1412,12369,12465,
65401,1391,12534,312,2454,1093,2326,2710,2582,1582,65190,65191,65192,999,2393,
2649,12920,12824,12906,12619,12810,3586,3589,3587,3588,3675,409,3590,13201,
12365,12461,65399,13077,13078,13076,12910,12814,12896,12593,12800,12595,1116,
7733,13208,13222,65355,13218,12371,13248,3585,12467,65402,13086,1153,12927,
835,9382,13226,1135,13263,670,12367,12463,65400,13240,13246,108,2482,314,2354,
2738,2610,3653,65276,65272,65271,65274,65273,65275,65270,65269,1604,955,411,
1500,64316,64316,1500,1500,1500,1500,1500,65246,64714,65247,64713,64715,65010,
65248,64904,64716,65247,65247,9711,410,620,12556,318,316,9435,7741,316,320,
320,7735,7737,794,792,60,8804,8922,65308,8818,8822,8806,65124,622,9612,621,
8356,1388,457,1113,63168,2355,2739,7739,2356,2529,2401,2531,2403,619,65356,
13264,3628,8743,172,8976,8744,3621,383,65102,818,65101,9674,9383,322,8467,
63214,9617,3622,2444,2316,2530,2402,13267,109,2478,175,817,772,717,65507,7743,
2350,2734,2606,1444,1444,12414,63637,63636,3659,63635,63628,63627,3656,63626,
63620,3633,63625,3655,63631,63630,3657,63629,63634,63633,3658,63632,3654,
12510,65423,9794,13127,1470,9794,1455,13187,12551,13268,9436,13221,7745,7747,
1605,65250,65251,65252,64721,64584,13133,12417,13182,12513,65426,1502,64318,
64318,1502,1396,1445,1446,1446,1445,625,13202,65381,183,12914,12818,12900,
12609,12656,12804,12654,12655,12415,12511,65424,8722,800,8854,727,8723,8242,
13130,13129,624,13206,13219,65357,13215,12418,13249,12514,65427,13270,3617,
13223,13224,9384,13227,13235,63215,623,181,181,13186,8811,8810,13196,956,
13197,12416,12512,65425,13205,215,13211,1443,1443,9834,9835,9837,9839,13234,
13238,13244,13241,13239,13247,13245,110,2472,8711,324,2344,2728,2600,12394,
12490,65413,329,13185,12555,160,328,326,9437,7755,326,7749,7751,12397,12493,
65416,8362,13195,2457,2329,2713,2585,3591,12435,626,627,12911,12815,12597,
12897,12598,12596,12648,12801,12647,12646,12395,12491,65414,63641,3661,57,
1641,2543,9320,10130,2415,2799,2671,1641,12329,12840,8329,65305,63289,9340,
9360,1785,8568,8313,9330,9350,9370,3673,460,1114,12531,65437,414,7753,65358,
13210,2467,2339,2723,2595,2345,12398,12494,65417,160,3603,3609,1606,65254,
1722,64415,65255,65255,64722,64587,65256,64725,64590,64653,8716,8713,8713,
8800,8815,8817,8825,8802,8814,8816,8742,8832,8836,8833,8837,1398,9385,13233,
8319,241,957,12396,12492,65415,2492,2364,2748,2620,35,65283,65119,884,885,
8470,1504,64320,64320,1504,13237,13243,2462,2334,2718,2590,111,243,3629,629,
1257,1259,2451,12571,335,2321,2705,2377,2761,466,9438,244,7889,7897,7891,7893,
7895,1086,337,525,2323,246,1255,7885,339,12634,731,808,242,2707,1413,12362,
7887,417,7899,7907,7901,7903,7905,337,419,527,12458,65397,12631,1451,333,7763,
7761,2384,969,982,1121,631,1147,1149,974,2768,959,972,65359,49,1633,2535,9312,
10122,2407,8228,8539,63196,2791,2663,1633,189,12321,12832,8321,65297,2548,
63281,9332,9352,1777,188,8560,185,3665,8531,491,493,2579,2635,596,9386,9702,
8997,170,186,8735,2322,2378,248,511,12361,12457,65387,511,63216,1151,245,7757,
7759,12577,8254,65098,773,65097,65100,65099,175,2507,2379,2763,112,13184,
13099,2474,7765,2346,8671,8670,2730,2602,12401,3631,12497,1156,1216,12671,182,
8741,40,64830,63725,63724,8333,65288,65113,8317,63723,65077,41,64831,63736,
63735,8334,65289,65114,8318,63734,65078,8706,1472,1433,13225,1463,1463,1463,
1463,1463,1463,1463,1463,1441,12550,9439,7767,1508,1087,64324,64324,13115,
64323,1662,1402,1508,64343,64344,12410,64345,12506,1191,64334,37,1642,65285,
65130,46,1417,183,65377,63207,65294,65106,63208,834,8869,8240,8359,13194,2475,
2347,2731,2603,966,981,12922,12826,12908,12621,12812,632,3642,981,421,3614,
3612,3616,960,12915,12819,12662,12901,12658,12610,12805,12660,12612,12661,
12663,12659,12404,12500,982,1411,43,799,8853,177,726,65291,65122,8314,65360,
13272,12413,9759,9756,9758,9757,12509,3611,12306,12320,9387,8826,8478,697,
8245,8719,8965,12540,8984,8834,8835,8759,8733,968,1137,1158,13232,12407,12503,
13236,13242,113,2392,1448,1602,65238,65239,65240,1464,1464,1464,1464,1464,
1464,1464,1464,1464,1464,1464,1464,1464,1464,1464,1464,1439,12561,9440,672,
65361,1511,64327,64327,1511,1511,1511,1511,1511,1511,1511,1511,1511,1511,1511,
1511,1511,1511,1511,1511,1511,1511,1511,1511,1511,9388,9833,1467,1467,1467,
1467,1467,1467,1467,1467,63,1567,1374,191,63423,894,65311,63295,34,8222,8220,
65282,12318,12317,8221,8216,8219,8219,8217,329,8218,39,65287,114,1404,2480,
341,2352,8730,63717,13230,13231,13229,1471,1471,2736,2608,12425,12521,65431,
2545,2544,612,8758,12566,345,343,9441,343,529,7769,7771,7773,8251,8838,8839,
174,63720,63194,1585,1408,65198,12428,1585,12524,65434,1512,64328,1512,1512,
1512,1512,1512,1512,1512,1512,1512,1512,1512,1512,1512,1512,1512,1512,1512,
1512,1512,1512,1512,8765,1431,1431,8976,638,639,2525,2397,961,637,635,693,
1009,734,12913,12817,12899,12608,12602,12649,12601,12603,12652,12803,12607,
12604,12651,12605,12606,12650,12653,8735,793,8895,12426,12522,65432,730,805,
778,703,1369,796,723,702,825,722,531,13137,7775,636,634,65362,12429,12525,
65435,3619,9389,2524,2353,2652,1681,64397,2528,2400,2784,2500,2372,2756,63217,
9616,633,692,12427,12523,65433,2546,2547,63197,3620,2443,2315,2699,2499,2371,
2755,115,2488,347,7781,1589,2360,65210,65211,65212,2744,2616,12373,12469,
65403,65018,1505,64321,64321,1505,3634,3649,3652,3651,3635,3632,3648,63622,
3637,63621,3636,3650,63624,3639,63623,3638,3640,3641,12569,353,7783,351,601,
1241,1243,602,9442,349,537,7777,7779,7785,828,8243,714,167,1587,65202,65203,
65204,1462,1462,1462,1462,1462,1462,1462,1426,1462,1405,12379,12475,65406,59,
1563,65307,65108,12444,65439,13090,13091,55,1639,2541,9318,10128,2413,8542,
2797,2669,1639,12327,12838,8327,65303,63287,9338,9358,1783,8566,8311,9328,
9348,9368,3671,173,1399,2486,1096,1617,64609,64606,64608,1617,64610,64607,
9618,9619,9617,9618,2358,2742,2614,1427,12565,1097,1588,65206,65207,65208,995,
8362,8362,1456,1456,1456,1456,1456,1456,1456,1456,1456,1211,1005,1513,64329,
64329,64300,64300,64301,64301,1473,1513,64298,64298,64299,64299,642,963,962,
962,1010,12375,12471,65404,1469,1469,8764,1474,12916,12820,12670,12902,12666,
12613,12667,12806,12669,12668,54,1638,2540,9317,10127,2412,2796,2668,1638,
12326,12837,8326,65302,63286,9337,9357,1782,8565,8310,9327,2553,9347,9367,
3670,47,65295,383,7835,9786,65363,1475,173,1100,12381,12477,65407,824,823,
3625,3624,3595,3626,32,32,9824,9824,9828,9390,827,13252,13213,9641,9636,13199,
13214,13262,13265,13266,13198,13269,13212,13217,9638,9639,9640,9637,9635,
13275,2487,2359,2743,12617,12677,12672,12594,12645,12611,12614,12600,63218,
163,65505,822,821,8834,8842,8838,8827,8715,12377,12473,65405,1618,8721,9788,
8835,8843,8839,13276,13180,116,2468,8868,8867,2340,2724,2596,1591,65218,65219,
12383,65220,13181,12479,65408,1600,964,1514,64330,64330,64330,1514,359,12554,
357,680,355,1670,64379,64380,64381,64380,9443,7793,355,7831,7787,7789,1090,
1197,1578,65174,64674,64524,65175,12390,64673,64523,1577,65172,65176,64676,
64526,64627,12486,65411,8481,9742,1440,1449,9321,12841,9341,9361,8569,679,
1496,64312,64312,1496,1205,1435,1435,2469,2341,2725,2597,1584,65196,63640,
63639,3660,63638,1579,65178,65179,65180,8707,8756,952,977,977,12921,12825,
12907,12620,12811,9324,9344,9364,3601,429,3602,254,3607,3600,3608,3606,1154,
1644,1644,51,1635,2537,9314,10124,2409,8540,2793,2665,1635,12323,12834,8323,
65299,2550,63283,9334,9354,1779,190,63198,8562,179,3667,13204,12385,12481,
65409,12912,12816,12898,12599,12802,732,816,771,771,864,8764,820,830,8855,
1430,1430,2672,1155,1407,7791,65364,1385,12392,12488,65412,741,745,742,744,
743,445,389,424,900,13095,3599,12308,65117,65081,12309,65118,65082,3605,427,
9391,8482,63722,63195,648,9660,9668,9658,9650,678,1510,64326,64326,1510,1094,
1461,1461,1461,1461,1461,1461,1461,1461,1115,63219,2463,2335,2719,2591,1657,
64359,64360,64361,2464,2336,2720,2592,647,12388,12484,65410,12387,12483,65391,
9323,9343,9363,8571,9331,21316,9351,9371,50,1634,2536,9313,10123,2408,8229,
8229,65072,2792,2664,1634,12322,12833,8322,65298,2549,63282,9333,9353,1778,
8561,443,178,3666,8532,117,250,649,2441,12584,365,468,9444,251,7799,1091,2385,
369,533,2313,252,472,7795,474,1265,476,470,7909,249,2697,2569,12358,7911,432,
7913,7921,7915,7917,7919,369,1267,535,12454,65395,1145,12636,363,1263,7803,
2625,65365,95,8215,65343,65075,65103,8746,8704,371,9392,9600,1476,965,971,944,
650,973,797,724,2675,367,1118,12357,12453,65385,1199,1201,361,7801,7797,2442,
2314,2698,2570,2626,2498,2370,2754,2497,2369,2753,118,2357,2741,2613,12535,
1493,64309,64309,64309,1493,64331,64331,1520,1521,9445,7807,1074,1700,64363,
64364,64365,12537,9792,124,781,809,716,712,1406,651,12536,2509,2381,2765,2435,
2307,2691,65366,1400,12446,12542,12443,65438,12538,9393,7805,652,12436,12532,
119,7811,12633,12431,12527,65436,12632,12430,12526,13143,12316,65076,1608,
65262,1572,65158,13277,9446,373,7813,7815,7817,12433,8472,12529,12638,12637,
7809,9702,9675,9689,12302,65091,12303,65092,9671,9672,9663,9661,9667,9665,
12310,12311,9657,9655,9643,9786,9633,9734,9743,12312,12313,9653,9651,12432,
12528,12639,65367,12434,12530,65382,8361,65510,3623,9394,7832,695,653,447,120,
829,12562,9447,7821,7819,1389,958,65368,9395,739,121,13134,2479,253,2351,
12626,2735,2607,12420,12516,65428,12625,3662,12419,12515,65388,1123,9448,375,
255,7823,7925,1610,1746,64431,65266,1574,65162,65163,65164,65267,65268,64733,
64600,64660,1745,12630,165,65509,12629,12678,1450,1450,1099,1273,12673,12675,
12674,1434,7923,436,7927,1397,1111,12642,9775,1410,65369,1497,64313,64313,
1497,1522,64287,12424,12681,12520,65430,12635,12423,12519,65390,1011,12680,
12679,3618,3597,9396,890,837,422,7833,696,7929,654,12422,12684,12518,65429,
12640,1131,1133,1127,1129,12421,12517,65389,12683,12682,2527,2399,122,1382,
378,2395,2651,1592,65222,65223,12374,65224,1586,65200,12470,1429,1428,1432,
1494,64310,64310,1494,12567,382,9449,7825,657,380,380,7827,1079,1177,1247,
12380,12476,48,1632,2534,2406,2790,2662,1632,8320,65296,63280,1776,8304,3664,
65279,8204,8203,950,12563,1386,1218,1078,1175,1245,12376,12472,1454,7829,
65370,12382,12478,9397,656,438,12378,12474,
};
 
static const unsigned short agl_dup_offsets[] = {
32,0,124,3,160,6,173,9,175,12,181,15,183,18,266,21,267,24,272,27,273,30,
278,33,279,36,288,39,289,42,290,45,291,48,304,51,310,54,311,57,315,60,316,63,
319,66,320,69,325,72,326,75,329,78,336,81,337,84,342,87,343,90,354,93,355,96,
368,99,369,102,379,105,380,108,383,111,510,114,511,117,700,120,701,123,
732,126,768,129,769,132,771,135,777,138,803,141,901,144,962,147,977,150,
978,153,981,156,982,159,1025,162,1026,165,1027,168,1028,171,1029,174,1030,177,
1031,180,1032,183,1033,186,1034,189,1035,192,1036,195,1038,198,1039,201,
1040,204,1041,207,1042,210,1043,213,1044,216,1045,219,1046,222,1047,225,
1048,228,1049,231,1050,234,1051,237,1052,240,1053,243,1054,246,1055,249,
1056,252,1057,255,1058,258,1059,261,1060,264,1061,267,1062,270,1063,273,
1064,276,1065,279,1066,282,1067,285,1068,288,1069,291,1070,294,1071,297,
1072,300,1073,303,1074,306,1075,309,1076,312,1077,315,1078,318,1079,321,
1080,324,1081,327,1082,330,1083,333,1084,336,1085,339,1086,342,1087,345,
1088,348,1089,351,1090,354,1091,357,1092,360,1093,363,1094,366,1095,369,
1096,372,1097,375,1098,378,1099,381,1100,384,1101,387,1102,390,1103,393,
1105,396,1106,399,1107,402,1108,405,1109,408,1110,411,1111,414,1112,417,
1113,420,1114,423,1115,426,1116,429,1118,432,1119,435,1122,438,1123,441,
1138,444,1139,447,1140,450,1141,453,1168,456,1169,459,1241,462,1425,465,
1430,470,1431,473,1435,476,1443,479,1444,482,1445,485,1446,488,1447,491,
1450,494,1456,497,1457,508,1458,518,1459,528,1460,538,1461,548,1462,558,
1463,568,1464,578,1465,596,1467,606,1468,616,1469,620,1470,624,1471,627,
1472,631,1473,634,1474,637,1475,640,1488,643,1489,647,1490,651,1491,655,
1492,679,1493,683,1494,687,1495,691,1496,695,1497,699,1498,703,1499,711,
1500,715,1501,723,1502,727,1503,731,1504,735,1505,739,1506,743,1507,747,
1508,751,1509,755,1510,759,1511,763,1512,787,1513,811,1514,815,1520,819,
1521,822,1522,825,1548,828,1563,831,1567,834,1569,837,1570,848,1571,851,
1572,854,1573,857,1574,860,1575,863,1576,866,1577,869,1578,872,1579,875,
1580,878,1581,881,1582,884,1583,887,1584,890,1585,893,1586,897,1587,900,
1588,903,1589,906,1590,909,1591,912,1592,915,1593,918,1594,921,1600,924,
1601,929,1602,932,1603,935,1604,938,1605,941,1606,944,1607,947,1608,950,
1609,953,1610,956,1611,959,1612,962,1613,966,1614,969,1615,973,1616,977,
1617,980,1618,984,1632,987,1633,991,1634,995,1635,999,1636,1003,1637,1007,
1638,1011,1639,1015,1640,1019,1641,1023,1642,1027,1643,1030,1644,1033,
1645,1036,1657,1040,1662,1043,1670,1046,1672,1049,1681,1052,1688,1055,
1700,1058,1711,1061,1722,1064,1729,1067,1746,1070,8204,1073,8213,1076,
8215,1079,8219,1082,8229,1085,8353,1088,8356,1091,8362,1094,8364,1099,
8453,1102,8467,1105,8470,1108,8486,1111,8616,1114,8656,1117,8658,1120,
8660,1123,8704,1126,8707,1129,8710,1132,8711,1135,8713,1138,8735,1141,
8764,1144,8773,1147,8834,1150,8835,1153,8838,1156,8839,1159,8853,1162,
8855,1165,8976,1168,8992,1171,8993,1174,9617,1177,9618,1180,9619,1183,
9632,1186,9633,1189,9642,1192,9643,1195,9644,1198,9650,1201,9658,1204,
9660,1207,9668,1210,9675,1213,9679,1216,9688,1219,9689,1222,9702,1225,
9786,1228,9787,1231,9788,1234,9792,1237,9794,1240,9824,1243,9827,1246,
9829,1249,9835,1252,64287,1255,64298,1260,64299,1264,64300,1268,64301,1271,
64305,1274,64306,1277,64307,1280,64308,1283,64309,1286,64310,1291,64312,1294,
64313,1297,64314,1300,64315,1303,64316,1306,64318,1309,64320,1312,64321,1315,
64324,1318,64326,1321,64327,1324,64329,1327,64330,1330,64331,1334,64380,1338,
65247,1341,65255,1345,65258,1348,65267,1351,65268,1354,
};
 
static const char *agl_dup_names[] = {
"space","spacehackarabic",0,"bar","verticalbar",0,"nbspace",
"nonbreakingspace",0,"sfthyphen","softhyphen",0,"macron","overscore",0,"mu",
"mu1",0,"middot","periodcentered",0,"Cdot","Cdotaccent",0,"cdot","cdotaccent",
0,"Dcroat","Dslash",0,"dcroat","dmacron",0,"Edot","Edotaccent",0,"edot",
"edotaccent",0,"Gdot","Gdotaccent",0,"gdot","gdotaccent",0,"Gcedilla",
"Gcommaaccent",0,"gcedilla","gcommaaccent",0,"Idot","Idotaccent",0,"Kcedilla",
"Kcommaaccent",0,"kcedilla","kcommaaccent",0,"Lcedilla","Lcommaaccent",0,
"lcedilla","lcommaaccent",0,"Ldot","Ldotaccent",0,"ldot","ldotaccent",0,
"Ncedilla","Ncommaaccent",0,"ncedilla","ncommaaccent",0,"napostrophe",
"quoterightn",0,"Odblacute","Ohungarumlaut",0,"odblacute","ohungarumlaut",0,
"Rcedilla","Rcommaaccent",0,"rcedilla","rcommaaccent",0,"Tcedilla",
"Tcommaaccent",0,"tcedilla","tcommaaccent",0,"Udblacute","Uhungarumlaut",0,
"udblacute","uhungarumlaut",0,"Zdot","Zdotaccent",0,"zdot","zdotaccent",0,
"longs","slong",0,"Oslashacute","Ostrokeacute",0,"oslashacute","ostrokeacute",
0,"afii57929","apostrophemod",0,"afii64937","commareversedmod",0,"ilde",
"tilde",0,"gravecmb","gravecomb",0,"acutecmb","acutecomb",0,"tildecmb",
"tildecomb",0,"hookabovecomb","hookcmb",0,"dotbelowcmb","dotbelowcomb",0,
"dialytikatonos","dieresistonos",0,"sigma1","sigmafinal",0,"theta1",
"thetasymbolgreek",0,"Upsilon1","Upsilonhooksymbol",0,"phi1","phisymbolgreek",
0,"omega1","pisymbolgreek",0,"Iocyrillic","afii10023",0,"Djecyrillic",
"afii10051",0,"Gjecyrillic","afii10052",0,"Ecyrillic","afii10053",0,
"Dzecyrillic","afii10054",0,"Icyrillic","afii10055",0,"Yicyrillic",
"afii10056",0,"Jecyrillic","afii10057",0,"Ljecyrillic","afii10058",0,
"Njecyrillic","afii10059",0,"Tshecyrillic","afii10060",0,"Kjecyrillic",
"afii10061",0,"Ushortcyrillic","afii10062",0,"Dzhecyrillic","afii10145",0,
"Acyrillic","afii10017",0,"Becyrillic","afii10018",0,"Vecyrillic","afii10019",
0,"Gecyrillic","afii10020",0,"Decyrillic","afii10021",0,"Iecyrillic",
"afii10022",0,"Zhecyrillic","afii10024",0,"Zecyrillic","afii10025",0,
"Iicyrillic","afii10026",0,"Iishortcyrillic","afii10027",0,"Kacyrillic",
"afii10028",0,"Elcyrillic","afii10029",0,"Emcyrillic","afii10030",0,
"Encyrillic","afii10031",0,"Ocyrillic","afii10032",0,"Pecyrillic","afii10033",
0,"Ercyrillic","afii10034",0,"Escyrillic","afii10035",0,"Tecyrillic",
"afii10036",0,"Ucyrillic","afii10037",0,"Efcyrillic","afii10038",0,
"Khacyrillic","afii10039",0,"Tsecyrillic","afii10040",0,"Checyrillic",
"afii10041",0,"Shacyrillic","afii10042",0,"Shchacyrillic","afii10043",0,
"Hardsigncyrillic","afii10044",0,"Yericyrillic","afii10045",0,
"Softsigncyrillic","afii10046",0,"Ereversedcyrillic","afii10047",0,
"IUcyrillic","afii10048",0,"IAcyrillic","afii10049",0,"acyrillic","afii10065",
0,"afii10066","becyrillic",0,"afii10067","vecyrillic",0,"afii10068",
"gecyrillic",0,"afii10069","decyrillic",0,"afii10070","iecyrillic",0,
"afii10072","zhecyrillic",0,"afii10073","zecyrillic",0,"afii10074",
"iicyrillic",0,"afii10075","iishortcyrillic",0,"afii10076","kacyrillic",0,
"afii10077","elcyrillic",0,"afii10078","emcyrillic",0,"afii10079",
"encyrillic",0,"afii10080","ocyrillic",0,"afii10081","pecyrillic",0,
"afii10082","ercyrillic",0,"afii10083","escyrillic",0,"afii10084",
"tecyrillic",0,"afii10085","ucyrillic",0,"afii10086","efcyrillic",0,
"afii10087","khacyrillic",0,"afii10088","tsecyrillic",0,"afii10089",
"checyrillic",0,"afii10090","shacyrillic",0,"afii10091","shchacyrillic",0,
"afii10092","hardsigncyrillic",0,"afii10093","yericyrillic",0,"afii10094",
"softsigncyrillic",0,"afii10095","ereversedcyrillic",0,"afii10096",
"iucyrillic",0,"afii10097","iacyrillic",0,"afii10071","iocyrillic",0,
"afii10099","djecyrillic",0,"afii10100","gjecyrillic",0,"afii10101",
"ecyrillic",0,"afii10102","dzecyrillic",0,"afii10103","icyrillic",0,
"afii10104","yicyrillic",0,"afii10105","jecyrillic",0,"afii10106",
"ljecyrillic",0,"afii10107","njecyrillic",0,"afii10108","tshecyrillic",0,
"afii10109","kjecyrillic",0,"afii10110","ushortcyrillic",0,"afii10193",
"dzhecyrillic",0,"Yatcyrillic","afii10146",0,"afii10194","yatcyrillic",0,
"Fitacyrillic","afii10147",0,"afii10195","fitacyrillic",0,"Izhitsacyrillic",
"afii10148",0,"afii10196","izhitsacyrillic",0,"Gheupturncyrillic","afii10050",
0,"afii10098","gheupturncyrillic",0,"afii10846","schwacyrillic",0,
"etnahtafoukhhebrew","etnahtafoukhlefthebrew","etnahtahebrew",
"etnahtalefthebrew",0,"tipehahebrew","tipehalefthebrew",0,"reviahebrew",
"reviamugrashhebrew",0,"tevirhebrew","tevirlefthebrew",0,"munahhebrew",
"munahlefthebrew",0,"mahapakhhebrew","mahapakhlefthebrew",0,"merkhahebrew",
"merkhalefthebrew",0,"merkhakefulahebrew","merkhakefulalefthebrew",0,
"dargahebrew","dargalefthebrew",0,"yerahbenyomohebrew",
"yerahbenyomolefthebrew",0,"afii57799","sheva","sheva115","sheva15","sheva22",
"sheva2e","shevahebrew","shevanarrowhebrew","shevaquarterhebrew",
"shevawidehebrew",0,"afii57801","hatafsegol","hatafsegol17","hatafsegol24",
"hatafsegol30","hatafsegolhebrew","hatafsegolnarrowhebrew",
"hatafsegolquarterhebrew","hatafsegolwidehebrew",0,"afii57800","hatafpatah",
"hatafpatah16","hatafpatah23","hatafpatah2f","hatafpatahhebrew",
"hatafpatahnarrowhebrew","hatafpatahquarterhebrew","hatafpatahwidehebrew",0,
"afii57802","hatafqamats","hatafqamats1b","hatafqamats28","hatafqamats34",
"hatafqamatshebrew","hatafqamatsnarrowhebrew","hatafqamatsquarterhebrew",
"hatafqamatswidehebrew",0,"afii57793","hiriq","hiriq14","hiriq21","hiriq2d",
"hiriqhebrew","hiriqnarrowhebrew","hiriqquarterhebrew","hiriqwidehebrew",0,
"afii57794","tsere","tsere12","tsere1e","tsere2b","tserehebrew",
"tserenarrowhebrew","tserequarterhebrew","tserewidehebrew",0,"afii57795",
"segol","segol13","segol1f","segol2c","segolhebrew","segolnarrowhebrew",
"segolquarterhebrew","segolwidehebrew",0,"afii57798","patah","patah11",
"patah1d","patah2a","patahhebrew","patahnarrowhebrew","patahquarterhebrew",
"patahwidehebrew",0,"afii57797","qamats","qamats10","qamats1a","qamats1c",
"qamats27","qamats29","qamats33","qamatsde","qamatshebrew",
"qamatsnarrowhebrew","qamatsqatanhebrew","qamatsqatannarrowhebrew",
"qamatsqatanquarterhebrew","qamatsqatanwidehebrew","qamatsquarterhebrew",
"qamatswidehebrew",0,"afii57806","holam","holam19","holam26","holam32",
"holamhebrew","holamnarrowhebrew","holamquarterhebrew","holamwidehebrew",0,
"afii57796","qubuts","qubuts18","qubuts25","qubuts31","qubutshebrew",
"qubutsnarrowhebrew","qubutsquarterhebrew","qubutswidehebrew",0,"afii57807",
"dagesh","dageshhebrew",0,"afii57839","siluqhebrew","siluqlefthebrew",0,
"afii57645","maqafhebrew",0,"afii57841","rafe","rafehebrew",0,"afii57842",
"paseqhebrew",0,"afii57804","shindothebrew",0,"afii57803","sindothebrew",0,
"afii57658","sofpasuqhebrew",0,"afii57664","alef","alefhebrew",0,"afii57665",
"bet","bethebrew",0,"afii57666","gimel","gimelhebrew",0,"afii57667","dalet",
"dalethatafpatah","dalethatafpatahhebrew","dalethatafsegol",
"dalethatafsegolhebrew","dalethebrew","dalethiriq","dalethiriqhebrew",
"daletholam","daletholamhebrew","daletpatah","daletpatahhebrew","daletqamats",
"daletqamatshebrew","daletqubuts","daletqubutshebrew","daletsegol",
"daletsegolhebrew","daletsheva","daletshevahebrew","dalettsere",
"dalettserehebrew",0,"afii57668","he","hehebrew",0,"afii57669","vav",
"vavhebrew",0,"afii57670","zayin","zayinhebrew",0,"afii57671","het",
"hethebrew",0,"afii57672","tet","tethebrew",0,"afii57673","yod","yodhebrew",0,
"afii57674","finalkaf","finalkafhebrew","finalkafqamats",
"finalkafqamatshebrew","finalkafsheva","finalkafshevahebrew",0,"afii57675",
"kaf","kafhebrew",0,"afii57676","lamed","lamedhebrew","lamedholam",
"lamedholamdagesh","lamedholamdageshhebrew","lamedholamhebrew",0,"afii57677",
"finalmem","finalmemhebrew",0,"afii57678","mem","memhebrew",0,"afii57679",
"finalnun","finalnunhebrew",0,"afii57680","nun","nunhebrew",0,"afii57681",
"samekh","samekhhebrew",0,"afii57682","ayin","ayinhebrew",0,"afii57683",
"finalpe","finalpehebrew",0,"afii57684","pe","pehebrew",0,"afii57685",
"finaltsadi","finaltsadihebrew",0,"afii57686","tsadi","tsadihebrew",0,
"afii57687","qof","qofhatafpatah","qofhatafpatahhebrew","qofhatafsegol",
"qofhatafsegolhebrew","qofhebrew","qofhiriq","qofhiriqhebrew","qofholam",
"qofholamhebrew","qofpatah","qofpatahhebrew","qofqamats","qofqamatshebrew",
"qofqubuts","qofqubutshebrew","qofsegol","qofsegolhebrew","qofsheva",
"qofshevahebrew","qoftsere","qoftserehebrew",0,"afii57688","resh",
"reshhatafpatah","reshhatafpatahhebrew","reshhatafsegol",
"reshhatafsegolhebrew","reshhebrew","reshhiriq","reshhiriqhebrew","reshholam",
"reshholamhebrew","reshpatah","reshpatahhebrew","reshqamats",
"reshqamatshebrew","reshqubuts","reshqubutshebrew","reshsegol",
"reshsegolhebrew","reshsheva","reshshevahebrew","reshtsere","reshtserehebrew",
0,"afii57689","shin","shinhebrew",0,"afii57690","tav","tavhebrew",0,
"afii57716","vavvavhebrew",0,"afii57717","vavyodhebrew",0,"afii57718",
"yodyodhebrew",0,"afii57388","commaarabic",0,"afii57403","semicolonarabic",0,
"afii57407","questionarabic",0,"afii57409","hamzaarabic","hamzadammaarabic",
"hamzadammatanarabic","hamzafathaarabic","hamzafathatanarabic",
"hamzalowarabic","hamzalowkasraarabic","hamzalowkasratanarabic",
"hamzasukunarabic",0,"afii57410","alefmaddaabovearabic",0,"afii57411",
"alefhamzaabovearabic",0,"afii57412","wawhamzaabovearabic",0,"afii57413",
"alefhamzabelowarabic",0,"afii57414","yehhamzaabovearabic",0,"afii57415",
"alefarabic",0,"afii57416","beharabic",0,"afii57417","tehmarbutaarabic",0,
"afii57418","teharabic",0,"afii57419","theharabic",0,"afii57420","jeemarabic",
0,"afii57421","haharabic",0,"afii57422","khaharabic",0,"afii57423",
"dalarabic",0,"afii57424","thalarabic",0,"afii57425","reharabic",
"rehyehaleflamarabic",0,"afii57426","zainarabic",0,"afii57427","seenarabic",0,
"afii57428","sheenarabic",0,"afii57429","sadarabic",0,"afii57430","dadarabic",
0,"afii57431","taharabic",0,"afii57432","zaharabic",0,"afii57433","ainarabic",
0,"afii57434","ghainarabic",0,"afii57440","kashidaautoarabic",
"kashidaautonosidebearingarabic","tatweelarabic",0,"afii57441","feharabic",0,
"afii57442","qafarabic",0,"afii57443","kafarabic",0,"afii57444","lamarabic",0,
"afii57445","meemarabic",0,"afii57446","noonarabic",0,"afii57470","heharabic",
0,"afii57448","wawarabic",0,"afii57449","alefmaksuraarabic",0,"afii57450",
"yeharabic",0,"afii57451","fathatanarabic",0,"afii57452",
"dammatanaltonearabic","dammatanarabic",0,"afii57453","kasratanarabic",0,
"afii57454","fathaarabic","fathalowarabic",0,"afii57455","dammaarabic",
"dammalowarabic",0,"afii57456","kasraarabic",0,"afii57457","shaddaarabic",
"shaddafathatanarabic",0,"afii57458","sukunarabic",0,"afii57392","zeroarabic",
"zerohackarabic",0,"afii57393","onearabic","onehackarabic",0,"afii57394",
"twoarabic","twohackarabic",0,"afii57395","threearabic","threehackarabic",0,
"afii57396","fourarabic","fourhackarabic",0,"afii57397","fivearabic",
"fivehackarabic",0,"afii57398","sixarabic","sixhackarabic",0,"afii57399",
"sevenarabic","sevenhackarabic",0,"afii57400","eightarabic","eighthackarabic",
0,"afii57401","ninearabic","ninehackarabic",0,"afii57381","percentarabic",0,
"decimalseparatorarabic","decimalseparatorpersian",0,
"thousandsseparatorarabic","thousandsseparatorpersian",0,"afii63167",
"asteriskaltonearabic","asteriskarabic",0,"afii57511","tteharabic",0,
"afii57506","peharabic",0,"afii57507","tcheharabic",0,"afii57512",
"ddalarabic",0,"afii57513","rreharabic",0,"afii57508","jeharabic",0,
"afii57505","veharabic",0,"afii57509","gafarabic",0,"afii57514",
"noonghunnaarabic",0,"haaltonearabic","hehaltonearabic",0,"afii57519",
"yehbarreearabic",0,"afii61664","zerowidthnonjoiner",0,"afii00208",
"horizontalbar",0,"dbllowline","underscoredbl",0,"quoteleftreversed",
"quotereversed",0,"twodotenleader","twodotleader",0,"colonmonetary",
"colonsign",0,"afii08941","lira",0,"afii57636","newsheqelsign","sheqel",
"sheqelhebrew",0,"Euro","euro",0,"afii61248","careof",0,"afii61289","lsquare",
0,"afii61352","numero",0,"Ohm","Omega",0,"arrowupdnbse","arrowupdownbase",0,
"arrowdblleft","arrowleftdbl",0,"arrowdblright","dblarrowright",0,
"arrowdblboth","dblarrowleft",0,"forall","universal",0,"existential",
"thereexists",0,"Delta","increment",0,"gradient","nabla",0,"notelement",
"notelementof",0,"orthogonal","rightangle",0,"similar","tildeoperator",0,
"approximatelyequal","congruent",0,"propersubset","subset",0,"propersuperset",
"superset",0,"reflexsubset","subsetorequal",0,"reflexsuperset",
"supersetorequal",0,"circleplus","pluscircle",0,"circlemultiply",
"timescircle",0,"logicalnotreversed","revlogicalnot",0,"integraltop",
"integraltp",0,"integralbottom","integralbt",0,"ltshade","shadelight",0,
"shade","shademedium",0,"dkshade","shadedark",0,"blacksquare","filledbox",0,
"H22073","whitesquare",0,"H18543","blacksmallsquare",0,"H18551",
"whitesmallsquare",0,"blackrectangle","filledrect",0,
"blackuppointingtriangle","triagup",0,"blackrightpointingpointer","triagrt",0,
"blackdownpointingtriangle","triagdn",0,"blackleftpointingpointer","triaglf",
0,"circle","whitecircle",0,"H18533","blackcircle",0,"bulletinverse",
"invbullet",0,"invcircle","whitecircleinverse",0,"openbullet","whitebullet",0,
"smileface","whitesmilingface",0,"blacksmilingface","invsmileface",0,
"compass","sun",0,"female","venus",0,"male","mars",0,"spade","spadesuitblack",
0,"club","clubsuitblack",0,"heart","heartsuitblack",0,"eighthnotebeamed",
"musicalnotedbl",0,"afii57705","doubleyodpatah","doubleyodpatahhebrew",
"yodyodpatahhebrew",0,"afii57694","shinshindot","shinshindothebrew",0,
"afii57695","shinsindot","shinsindothebrew",0,"shindageshshindot",
"shindageshshindothebrew",0,"shindageshsindot","shindageshsindothebrew",0,
"betdagesh","betdageshhebrew",0,"gimeldagesh","gimeldageshhebrew",0,
"daletdagesh","daletdageshhebrew",0,"hedagesh","hedageshhebrew",0,"afii57723",
"vavdagesh","vavdagesh65","vavdageshhebrew",0,"zayindagesh",
"zayindageshhebrew",0,"tetdagesh","tetdageshhebrew",0,"yoddagesh",
"yoddageshhebrew",0,"finalkafdagesh","finalkafdageshhebrew",0,"kafdagesh",
"kafdageshhebrew",0,"lameddagesh","lameddageshhebrew",0,"memdagesh",
"memdageshhebrew",0,"nundagesh","nundageshhebrew",0,"samekhdagesh",
"samekhdageshhebrew",0,"pedagesh","pedageshhebrew",0,"tsadidagesh",
"tsadidageshhebrew",0,"qofdagesh","qofdageshhebrew",0,"shindagesh",
"shindageshhebrew",0,"tavdages","tavdagesh","tavdageshhebrew",0,"afii57700",
"vavholam","vavholamhebrew",0,"tchehinitialarabic","tchehmeeminitialarabic",0,
"laminitialarabic","lammeemjeeminitialarabic","lammeemkhahinitialarabic",0,
"noonhehinitialarabic","nooninitialarabic",0,"hehfinalalttwoarabic",
"hehfinalarabic",0,"alefmaksurainitialarabic","yehinitialarabic",0,
"alefmaksuramedialarabic","yehmedialarabic",0,
};
/contrib/media/updf/pdf/mupdf.h
0,0 → 1,493
#ifndef _MUPDF_H_
#define _MUPDF_H_
 
#ifndef _FITZ_H_
#error "fitz.h must be included before mupdf.h"
#endif
 
typedef struct pdf_xref_s pdf_xref;
 
/*
* tokenizer and low-level object parser
*/
 
enum
{
PDF_TOK_ERROR, PDF_TOK_EOF,
PDF_TOK_OPEN_ARRAY, PDF_TOK_CLOSE_ARRAY,
PDF_TOK_OPEN_DICT, PDF_TOK_CLOSE_DICT,
PDF_TOK_OPEN_BRACE, PDF_TOK_CLOSE_BRACE,
PDF_TOK_NAME, PDF_TOK_INT, PDF_TOK_REAL, PDF_TOK_STRING, PDF_TOK_KEYWORD,
PDF_TOK_R, PDF_TOK_TRUE, PDF_TOK_FALSE, PDF_TOK_NULL,
PDF_TOK_OBJ, PDF_TOK_ENDOBJ,
PDF_TOK_STREAM, PDF_TOK_ENDSTREAM,
PDF_TOK_XREF, PDF_TOK_TRAILER, PDF_TOK_STARTXREF,
PDF_NUM_TOKENS
};
 
fz_error pdf_lex(int *tok, fz_stream *f, char *buf, int n, int *len);
 
fz_error pdf_parse_array(fz_obj **op, pdf_xref *xref, fz_stream *f, char *buf, int cap);
fz_error pdf_parse_dict(fz_obj **op, pdf_xref *xref, fz_stream *f, char *buf, int cap);
fz_error pdf_parse_stm_obj(fz_obj **op, pdf_xref *xref, fz_stream *f, char *buf, int cap);
fz_error pdf_parse_ind_obj(fz_obj **op, pdf_xref *xref, fz_stream *f, char *buf, int cap, int *num, int *gen, int *stm_ofs);
 
fz_rect pdf_to_rect(fz_obj *array);
fz_matrix pdf_to_matrix(fz_obj *array);
char *pdf_to_utf8(fz_obj *src);
unsigned short *pdf_to_ucs2(fz_obj *src);
fz_obj *pdf_to_utf8_name(fz_obj *src);
char *pdf_from_ucs2(unsigned short *str);
 
/*
* xref and object / stream api
*/
 
typedef struct pdf_xref_entry_s pdf_xref_entry;
typedef struct pdf_crypt_s pdf_crypt;
 
struct pdf_xref_entry_s
{
int ofs; /* file offset / objstm object number */
int gen; /* generation / objstm index */
int stm_ofs; /* on-disk stream */
fz_obj *obj; /* stored/cached object */
int type; /* 0=unset (f)ree i(n)use (o)bjstm */
};
 
struct pdf_xref_s
{
fz_stream *file;
int version;
int startxref;
int file_size;
pdf_crypt *crypt;
fz_obj *trailer;
 
int len;
pdf_xref_entry *table;
 
int page_len;
int page_cap;
fz_obj **page_objs;
fz_obj **page_refs;
 
struct pdf_store_s *store;
 
char scratch[65536];
};
 
fz_obj *pdf_resolve_indirect(fz_obj *ref);
fz_error pdf_cache_object(pdf_xref *, int num, int gen);
fz_error pdf_load_object(fz_obj **objp, pdf_xref *, int num, int gen);
void pdf_update_object( pdf_xref *xref, int num, int gen, fz_obj *newobj);
 
int pdf_is_stream(pdf_xref *xref, int num, int gen);
fz_stream *pdf_open_inline_stream(fz_stream *chain, pdf_xref *xref, fz_obj *stmobj, int length);
fz_error pdf_load_raw_stream(fz_buffer **bufp, pdf_xref *xref, int num, int gen);
fz_error pdf_load_stream(fz_buffer **bufp, pdf_xref *xref, int num, int gen);
fz_error pdf_open_raw_stream(fz_stream **stmp, pdf_xref *, int num, int gen);
fz_error pdf_open_stream(fz_stream **stmp, pdf_xref *, int num, int gen);
fz_error pdf_open_stream_at(fz_stream **stmp, pdf_xref *xref, int num, int gen, fz_obj *dict, int stm_ofs);
 
fz_error pdf_open_xref_with_stream(pdf_xref **xrefp, fz_stream *file, char *password);
fz_error pdf_open_xref(pdf_xref **xrefp, const char *filename, char *password);
void pdf_free_xref(pdf_xref *);
 
/* private */
fz_error pdf_repair_xref(pdf_xref *xref, char *buf, int bufsize);
fz_error pdf_repair_obj_stms(pdf_xref *xref);
void pdf_debug_xref(pdf_xref *);
void pdf_resize_xref(pdf_xref *xref, int newcap);
 
/*
* Encryption
*/
 
enum
{
PDF_PERM_PRINT = 1 << 2,
PDF_PERM_CHANGE = 1 << 3,
PDF_PERM_COPY = 1 << 4,
PDF_PERM_NOTES = 1 << 5,
PDF_PERM_FILL_FORM = 1 << 8,
PDF_PERM_ACCESSIBILITY = 1 << 9,
PDF_PERM_ASSEMBLE = 1 << 10,
PDF_PERM_HIGH_RES_PRINT = 1 << 11,
PDF_DEFAULT_PERM_FLAGS = 0xfffc
};
 
fz_error pdf_new_crypt(pdf_crypt **cp, fz_obj *enc, fz_obj *id);
void pdf_free_crypt(pdf_crypt *crypt);
 
void pdf_crypt_obj(pdf_crypt *crypt, fz_obj *obj, int num, int gen);
fz_stream *pdf_open_crypt(fz_stream *chain, pdf_crypt *crypt, int num, int gen);
fz_stream *pdf_open_crypt_with_filter(fz_stream *chain, pdf_crypt *crypt, char *name, int num, int gen);
 
int pdf_needs_password(pdf_xref *xref);
int pdf_authenticate_password(pdf_xref *xref, char *pw);
int pdf_has_permission(pdf_xref *xref, int p);
 
int pdf_get_crypt_revision(pdf_xref *xref);
char *pdf_get_crypt_method(pdf_xref *xref);
int pdf_get_crypt_length(pdf_xref *xref);
unsigned char *pdf_get_crypt_key(pdf_xref *xref);
 
void pdf_debug_crypt(pdf_crypt *crypt);
 
/*
* Resource store
*/
 
typedef struct pdf_store_s pdf_store;
 
pdf_store *pdf_new_store(void);
void pdf_free_store(pdf_store *store);
void pdf_debug_store(pdf_store *store);
 
void pdf_store_item(pdf_store *store, void *keepfn, void *dropfn, fz_obj *key, void *val);
void *pdf_find_item(pdf_store *store, void *dropfn, fz_obj *key);
void pdf_remove_item(pdf_store *store, void *dropfn, fz_obj *key);
void pdf_age_store(pdf_store *store, int maxage);
 
/*
* Functions, Colorspaces, Shadings and Images
*/
 
typedef struct pdf_function_s pdf_function;
 
fz_error pdf_load_function(pdf_function **func, pdf_xref *xref, fz_obj *ref);
void pdf_eval_function(pdf_function *func, float *in, int inlen, float *out, int outlen);
pdf_function *pdf_keep_function(pdf_function *func);
void pdf_drop_function(pdf_function *func);
 
fz_error pdf_load_colorspace(fz_colorspace **csp, pdf_xref *xref, fz_obj *obj);
fz_pixmap *pdf_expand_indexed_pixmap(fz_pixmap *src);
 
fz_error pdf_load_shading(fz_shade **shadep, pdf_xref *xref, fz_obj *obj);
 
fz_error pdf_load_inline_image(fz_pixmap **imgp, pdf_xref *xref, fz_obj *rdb, fz_obj *dict, fz_stream *file);
fz_error pdf_load_image(fz_pixmap **imgp, pdf_xref *xref, fz_obj *obj);
int pdf_is_jpx_image(fz_obj *dict);
 
/*
* Pattern
*/
 
typedef struct pdf_pattern_s pdf_pattern;
 
struct pdf_pattern_s
{
int refs;
int ismask;
float xstep;
float ystep;
fz_matrix matrix;
fz_rect bbox;
fz_obj *resources;
fz_buffer *contents;
};
 
fz_error pdf_load_pattern(pdf_pattern **patp, pdf_xref *xref, fz_obj *obj);
pdf_pattern *pdf_keep_pattern(pdf_pattern *pat);
void pdf_drop_pattern(pdf_pattern *pat);
 
/*
* XObject
*/
 
typedef struct pdf_xobject_s pdf_xobject;
 
struct pdf_xobject_s
{
int refs;
fz_matrix matrix;
fz_rect bbox;
int isolated;
int knockout;
int transparency;
fz_colorspace *colorspace;
fz_obj *resources;
fz_buffer *contents;
};
 
fz_error pdf_load_xobject(pdf_xobject **xobjp, pdf_xref *xref, fz_obj *obj);
pdf_xobject *pdf_keep_xobject(pdf_xobject *xobj);
void pdf_drop_xobject(pdf_xobject *xobj);
 
/*
* CMap
*/
 
typedef struct pdf_cmap_s pdf_cmap;
typedef struct pdf_range_s pdf_range;
 
enum { PDF_CMAP_SINGLE, PDF_CMAP_RANGE, PDF_CMAP_TABLE, PDF_CMAP_MULTI };
 
struct pdf_range_s
{
unsigned short low;
/* Next, we pack 2 fields into the same unsigned short. Top 14 bits
* are the extent, bottom 2 bits are flags: single, range, table,
* multi */
unsigned short extent_flags;
unsigned short offset; /* range-delta or table-index */
};
 
struct pdf_cmap_s
{
int refs;
char cmap_name[32];
 
char usecmap_name[32];
pdf_cmap *usecmap;
 
int wmode;
 
int codespace_len;
struct
{
unsigned short n;
unsigned short low;
unsigned short high;
} codespace[40];
 
int rlen, rcap;
pdf_range *ranges;
 
int tlen, tcap;
unsigned short *table;
};
 
pdf_cmap *pdf_new_cmap(void);
pdf_cmap *pdf_keep_cmap(pdf_cmap *cmap);
void pdf_drop_cmap(pdf_cmap *cmap);
 
void pdf_debug_cmap(pdf_cmap *cmap);
int pdf_get_wmode(pdf_cmap *cmap);
void pdf_set_wmode(pdf_cmap *cmap, int wmode);
void pdf_set_usecmap(pdf_cmap *cmap, pdf_cmap *usecmap);
 
void pdf_add_codespace(pdf_cmap *cmap, int low, int high, int n);
void pdf_map_range_to_table(pdf_cmap *cmap, int low, int *map, int len);
void pdf_map_range_to_range(pdf_cmap *cmap, int srclo, int srchi, int dstlo);
void pdf_map_one_to_many(pdf_cmap *cmap, int one, int *many, int len);
void pdf_sort_cmap(pdf_cmap *cmap);
 
int pdf_lookup_cmap(pdf_cmap *cmap, int cpt);
int pdf_lookup_cmap_full(pdf_cmap *cmap, int cpt, int *out);
unsigned char *pdf_decode_cmap(pdf_cmap *cmap, unsigned char *s, int *cpt);
 
pdf_cmap *pdf_new_identity_cmap(int wmode, int bytes);
fz_error pdf_parse_cmap(pdf_cmap **cmapp, fz_stream *file);
fz_error pdf_load_embedded_cmap(pdf_cmap **cmapp, pdf_xref *xref, fz_obj *ref);
fz_error pdf_load_system_cmap(pdf_cmap **cmapp, char *name);
pdf_cmap *pdf_find_builtin_cmap(char *cmap_name);
 
/*
* Font
*/
 
enum
{
PDF_FD_FIXED_PITCH = 1 << 0,
PDF_FD_SERIF = 1 << 1,
PDF_FD_SYMBOLIC = 1 << 2,
PDF_FD_SCRIPT = 1 << 3,
PDF_FD_NONSYMBOLIC = 1 << 5,
PDF_FD_ITALIC = 1 << 6,
PDF_FD_ALL_CAP = 1 << 16,
PDF_FD_SMALL_CAP = 1 << 17,
PDF_FD_FORCE_BOLD = 1 << 18
};
 
enum { PDF_ROS_CNS, PDF_ROS_GB, PDF_ROS_JAPAN, PDF_ROS_KOREA };
 
void pdf_load_encoding(char **estrings, char *encoding);
int pdf_lookup_agl(char *name);
const char **pdf_lookup_agl_duplicates(int ucs);
 
extern const unsigned short pdf_doc_encoding[256];
extern const char * const pdf_mac_roman[256];
extern const char * const pdf_mac_expert[256];
extern const char * const pdf_win_ansi[256];
extern const char * const pdf_standard[256];
 
typedef struct pdf_font_desc_s pdf_font_desc;
typedef struct pdf_hmtx_s pdf_hmtx;
typedef struct pdf_vmtx_s pdf_vmtx;
 
struct pdf_hmtx_s
{
unsigned short lo;
unsigned short hi;
int w; /* type3 fonts can be big! */
};
 
struct pdf_vmtx_s
{
unsigned short lo;
unsigned short hi;
short x;
short y;
short w;
};
 
struct pdf_font_desc_s
{
int refs;
 
fz_font *font;
 
/* FontDescriptor */
int flags;
float italic_angle;
float ascent;
float descent;
float cap_height;
float x_height;
float missing_width;
 
/* Encoding (CMap) */
pdf_cmap *encoding;
pdf_cmap *to_ttf_cmap;
int cid_to_gid_len;
unsigned short *cid_to_gid;
 
/* ToUnicode */
pdf_cmap *to_unicode;
int cid_to_ucs_len;
unsigned short *cid_to_ucs;
 
/* Metrics (given in the PDF file) */
int wmode;
 
int hmtx_len, hmtx_cap;
pdf_hmtx dhmtx;
pdf_hmtx *hmtx;
 
int vmtx_len, vmtx_cap;
pdf_vmtx dvmtx;
pdf_vmtx *vmtx;
 
int is_embedded;
};
 
void pdf_set_font_wmode(pdf_font_desc *font, int wmode);
void pdf_set_default_hmtx(pdf_font_desc *font, int w);
void pdf_set_default_vmtx(pdf_font_desc *font, int y, int w);
void pdf_add_hmtx(pdf_font_desc *font, int lo, int hi, int w);
void pdf_add_vmtx(pdf_font_desc *font, int lo, int hi, int x, int y, int w);
void pdf_end_hmtx(pdf_font_desc *font);
void pdf_end_vmtx(pdf_font_desc *font);
pdf_hmtx pdf_get_hmtx(pdf_font_desc *font, int cid);
pdf_vmtx pdf_get_vmtx(pdf_font_desc *font, int cid);
 
fz_error pdf_load_to_unicode(pdf_font_desc *font, pdf_xref *xref, char **strings, char *collection, fz_obj *cmapstm);
 
int pdf_font_cid_to_gid(pdf_font_desc *fontdesc, int cid);
 
unsigned char *pdf_find_builtin_font(char *name, unsigned int *len);
unsigned char *pdf_find_substitute_font(int mono, int serif, int bold, int italic, unsigned int *len);
unsigned char *pdf_find_substitute_cjk_font(int ros, int serif, unsigned int *len);
 
fz_error pdf_load_type3_font(pdf_font_desc **fontp, pdf_xref *xref, fz_obj *rdb, fz_obj *obj);
fz_error pdf_load_font(pdf_font_desc **fontp, pdf_xref *xref, fz_obj *rdb, fz_obj *obj);
 
pdf_font_desc *pdf_new_font_desc(void);
pdf_font_desc *pdf_keep_font(pdf_font_desc *fontdesc);
void pdf_drop_font(pdf_font_desc *font);
 
void pdf_debug_font(pdf_font_desc *fontdesc);
 
/*
* Interactive features
*/
 
typedef struct pdf_link_s pdf_link;
typedef struct pdf_annot_s pdf_annot;
typedef struct pdf_outline_s pdf_outline;
 
typedef enum pdf_link_kind_e
{
PDF_LINK_GOTO = 0,
PDF_LINK_URI,
PDF_LINK_LAUNCH,
PDF_LINK_NAMED,
PDF_LINK_ACTION,
} pdf_link_kind;
 
struct pdf_link_s
{
pdf_link_kind kind;
fz_rect rect;
fz_obj *dest;
pdf_link *next;
};
 
struct pdf_annot_s
{
fz_obj *obj;
fz_rect rect;
pdf_xobject *ap;
fz_matrix matrix;
pdf_annot *next;
};
 
struct pdf_outline_s
{
char *title;
pdf_link *link;
int count;
pdf_outline *child;
pdf_outline *next;
};
 
fz_obj *pdf_lookup_dest(pdf_xref *xref, fz_obj *needle);
fz_obj *pdf_lookup_name(pdf_xref *xref, char *which, fz_obj *needle);
fz_obj *pdf_load_name_tree(pdf_xref *xref, char *which);
 
pdf_outline *pdf_load_outline(pdf_xref *xref);
void pdf_debug_outline(pdf_outline *outline, int level);
void pdf_free_outline(pdf_outline *outline);
 
pdf_link *pdf_load_link(pdf_xref *xref, fz_obj *dict);
void pdf_load_links(pdf_link **, pdf_xref *, fz_obj *annots);
void pdf_free_link(pdf_link *link);
 
void pdf_load_annots(pdf_annot **, pdf_xref *, fz_obj *annots);
void pdf_free_annot(pdf_annot *link);
 
/*
* Page tree, pages and related objects
*/
 
typedef struct pdf_page_s pdf_page;
 
struct pdf_page_s
{
fz_rect mediabox;
int rotate;
int transparency;
fz_obj *resources;
fz_buffer *contents;
pdf_link *links;
pdf_annot *annots;
};
 
fz_error pdf_load_page_tree(pdf_xref *xref);
int pdf_find_page_number(pdf_xref *xref, fz_obj *pageobj);
int pdf_count_pages(pdf_xref *xref);
 
fz_error pdf_load_page(pdf_page **pagep, pdf_xref *xref, int number);
void pdf_free_page(pdf_page *page);
 
/*
* Content stream parsing
*/
 
fz_error pdf_run_page_with_usage(pdf_xref *xref, pdf_page *page, fz_device *dev, fz_matrix ctm, char *target);
fz_error pdf_run_page(pdf_xref *xref, pdf_page *page, fz_device *dev, fz_matrix ctm);
fz_error pdf_run_glyph(pdf_xref *xref, fz_obj *resources, fz_buffer *contents, fz_device *dev, fz_matrix ctm);
 
#endif
/contrib/media/updf/pdf/pdf_annot.c
0,0 → 1,234
#include "fitz.h"
#include "mupdf.h"
 
void
pdf_free_link(pdf_link *link)
{
if (link->next)
pdf_free_link(link->next);
if (link->dest)
fz_drop_obj(link->dest);
fz_free(link);
}
 
static fz_obj *
resolve_dest(pdf_xref *xref, fz_obj *dest)
{
if (fz_is_name(dest) || fz_is_string(dest))
{
dest = pdf_lookup_dest(xref, dest);
return resolve_dest(xref, dest);
}
 
else if (fz_is_array(dest))
{
return dest;
}
 
else if (fz_is_dict(dest))
{
dest = fz_dict_gets(dest, "D");
return resolve_dest(xref, dest);
}
 
else if (fz_is_indirect(dest))
return dest;
 
return NULL;
}
 
pdf_link *
pdf_load_link(pdf_xref *xref, fz_obj *dict)
{
fz_obj *dest;
fz_obj *action;
fz_obj *obj;
fz_rect bbox;
pdf_link_kind kind;
 
dest = NULL;
 
obj = fz_dict_gets(dict, "Rect");
if (obj)
bbox = pdf_to_rect(obj);
else
bbox = fz_empty_rect;
 
obj = fz_dict_gets(dict, "Dest");
if (obj)
{
kind = PDF_LINK_GOTO;
dest = resolve_dest(xref, obj);
}
 
action = fz_dict_gets(dict, "A");
 
/* fall back to additional action button's down/up action */
if (!action)
action = fz_dict_getsa(fz_dict_gets(dict, "AA"), "U", "D");
 
if (action)
{
obj = fz_dict_gets(action, "S");
if (fz_is_name(obj) && !strcmp(fz_to_name(obj), "GoTo"))
{
kind = PDF_LINK_GOTO;
dest = resolve_dest(xref, fz_dict_gets(action, "D"));
}
else if (fz_is_name(obj) && !strcmp(fz_to_name(obj), "URI"))
{
kind = PDF_LINK_URI;
dest = fz_dict_gets(action, "URI");
}
else if (fz_is_name(obj) && !strcmp(fz_to_name(obj), "Launch"))
{
kind = PDF_LINK_LAUNCH;
dest = fz_dict_gets(action, "F");
}
else if (fz_is_name(obj) && !strcmp(fz_to_name(obj), "Named"))
{
kind = PDF_LINK_NAMED;
dest = fz_dict_gets(action, "N");
}
else if (fz_is_name(obj) && (!strcmp(fz_to_name(obj), "GoToR")))
{
kind = PDF_LINK_ACTION;
dest = action;
}
else
{
dest = NULL;
}
}
 
if (dest)
{
pdf_link *link = fz_malloc(sizeof(pdf_link));
link->kind = kind;
link->rect = bbox;
link->dest = fz_keep_obj(dest);
link->next = NULL;
return link;
}
 
return NULL;
}
 
void
pdf_load_links(pdf_link **linkp, pdf_xref *xref, fz_obj *annots)
{
pdf_link *link, *head, *tail;
fz_obj *obj;
int i;
 
head = tail = NULL;
link = NULL;
 
for (i = 0; i < fz_array_len(annots); i++)
{
obj = fz_array_get(annots, i);
link = pdf_load_link(xref, obj);
if (link)
{
if (!head)
head = tail = link;
else
{
tail->next = link;
tail = link;
}
}
}
 
*linkp = head;
}
 
void
pdf_free_annot(pdf_annot *annot)
{
if (annot->next)
pdf_free_annot(annot->next);
if (annot->ap)
pdf_drop_xobject(annot->ap);
if (annot->obj)
fz_drop_obj(annot->obj);
fz_free(annot);
}
 
static void
pdf_transform_annot(pdf_annot *annot)
{
fz_matrix matrix = annot->ap->matrix;
fz_rect bbox = annot->ap->bbox;
fz_rect rect = annot->rect;
float w, h, x, y;
 
bbox = fz_transform_rect(matrix, bbox);
w = (rect.x1 - rect.x0) / (bbox.x1 - bbox.x0);
h = (rect.y1 - rect.y0) / (bbox.y1 - bbox.y0);
x = rect.x0 - bbox.x0;
y = rect.y0 - bbox.y0;
 
annot->matrix = fz_concat(fz_scale(w, h), fz_translate(x, y));
}
 
void
pdf_load_annots(pdf_annot **annotp, pdf_xref *xref, fz_obj *annots)
{
pdf_annot *annot, *head, *tail;
fz_obj *obj, *ap, *as, *n, *rect;
pdf_xobject *form;
fz_error error;
int i;
 
head = tail = NULL;
annot = NULL;
 
for (i = 0; i < fz_array_len(annots); i++)
{
obj = fz_array_get(annots, i);
 
rect = fz_dict_gets(obj, "Rect");
ap = fz_dict_gets(obj, "AP");
as = fz_dict_gets(obj, "AS");
if (fz_is_dict(ap))
{
n = fz_dict_gets(ap, "N"); /* normal state */
 
/* lookup current state in sub-dictionary */
if (!pdf_is_stream(xref, fz_to_num(n), fz_to_gen(n)))
n = fz_dict_get(n, as);
 
if (pdf_is_stream(xref, fz_to_num(n), fz_to_gen(n)))
{
error = pdf_load_xobject(&form, xref, n);
if (error)
{
fz_catch(error, "ignoring broken annotation");
continue;
}
 
annot = fz_malloc(sizeof(pdf_annot));
annot->obj = fz_keep_obj(obj);
annot->rect = pdf_to_rect(rect);
annot->ap = form;
annot->next = NULL;
 
pdf_transform_annot(annot);
 
if (annot)
{
if (!head)
head = tail = annot;
else
{
tail->next = annot;
tail = annot;
}
}
}
}
}
 
*annotp = head;
}
/contrib/media/updf/pdf/pdf_cmap.c
0,0 → 1,510
/*
* The CMap data structure here is constructed on the fly by
* adding simple range-to-range mappings. Then the data structure
* is optimized to contain both range-to-range and range-to-table
* lookups.
*
* Any one-to-many mappings are inserted as one-to-table
* lookups in the beginning, and are not affected by the optimization
* stage.
*
* There is a special function to add a 256-length range-to-table mapping.
* The ranges do not have to be added in order.
*
* This code can be a lot simpler if we don't care about wasting memory,
* or can trust the parser to give us optimal mappings.
*/
 
#include "fitz.h"
#include "mupdf.h"
 
/* Macros for accessing the combined extent_flags field */
#define pdf_range_high(r) ((r)->low + ((r)->extent_flags >> 2))
#define pdf_range_flags(r) ((r)->extent_flags & 3)
#define pdf_range_set_high(r, h) \
((r)->extent_flags = (((r)->extent_flags & 3) | ((h - (r)->low) << 2)))
#define pdf_range_set_flags(r, f) \
((r)->extent_flags = (((r)->extent_flags & ~3) | f))
 
/*
* Allocate, destroy and simple parameters.
*/
 
pdf_cmap *
pdf_new_cmap(void)
{
pdf_cmap *cmap;
 
cmap = fz_malloc(sizeof(pdf_cmap));
cmap->refs = 1;
 
strcpy(cmap->cmap_name, "");
strcpy(cmap->usecmap_name, "");
cmap->usecmap = NULL;
cmap->wmode = 0;
cmap->codespace_len = 0;
 
cmap->rlen = 0;
cmap->rcap = 0;
cmap->ranges = NULL;
 
cmap->tlen = 0;
cmap->tcap = 0;
cmap->table = NULL;
 
return cmap;
}
 
pdf_cmap *
pdf_keep_cmap(pdf_cmap *cmap)
{
if (cmap->refs >= 0)
cmap->refs ++;
return cmap;
}
 
void
pdf_drop_cmap(pdf_cmap *cmap)
{
if (cmap->refs >= 0)
{
if (--cmap->refs == 0)
{
if (cmap->usecmap)
pdf_drop_cmap(cmap->usecmap);
fz_free(cmap->ranges);
fz_free(cmap->table);
fz_free(cmap);
}
}
}
 
void
pdf_set_usecmap(pdf_cmap *cmap, pdf_cmap *usecmap)
{
int i;
 
if (cmap->usecmap)
pdf_drop_cmap(cmap->usecmap);
cmap->usecmap = pdf_keep_cmap(usecmap);
 
if (cmap->codespace_len == 0)
{
cmap->codespace_len = usecmap->codespace_len;
for (i = 0; i < usecmap->codespace_len; i++)
cmap->codespace[i] = usecmap->codespace[i];
}
}
 
int
pdf_get_wmode(pdf_cmap *cmap)
{
return cmap->wmode;
}
 
void
pdf_set_wmode(pdf_cmap *cmap, int wmode)
{
cmap->wmode = wmode;
}
 
void
pdf_debug_cmap(pdf_cmap *cmap)
{
int i, k, n;
 
printf("cmap $%p /%s {\n", (void *) cmap, cmap->cmap_name);
 
if (cmap->usecmap_name[0])
printf("\tusecmap /%s\n", cmap->usecmap_name);
if (cmap->usecmap)
printf("\tusecmap $%p\n", (void *) cmap->usecmap);
 
printf("\twmode %d\n", cmap->wmode);
 
printf("\tcodespaces {\n");
for (i = 0; i < cmap->codespace_len; i++)
{
printf("\t\t<%x> <%x>\n", cmap->codespace[i].low, cmap->codespace[i].high);
}
printf("\t}\n");
 
printf("\tranges (%d,%d) {\n", cmap->rlen, cmap->tlen);
for (i = 0; i < cmap->rlen; i++)
{
pdf_range *r = &cmap->ranges[i];
printf("\t\t<%04x> <%04x> ", r->low, pdf_range_high(r));
if (pdf_range_flags(r) == PDF_CMAP_TABLE)
{
printf("[ ");
for (k = 0; k < pdf_range_high(r) - r->low + 1; k++)
printf("%d ", cmap->table[r->offset + k]);
printf("]\n");
}
else if (pdf_range_flags(r) == PDF_CMAP_MULTI)
{
printf("< ");
n = cmap->table[r->offset];
for (k = 0; k < n; k++)
printf("%04x ", cmap->table[r->offset + 1 + k]);
printf(">\n");
}
else
printf("%d\n", r->offset);
}
printf("\t}\n}\n");
}
 
/*
* Add a codespacerange section.
* These ranges are used by pdf_decode_cmap to decode
* multi-byte encoded strings.
*/
void
pdf_add_codespace(pdf_cmap *cmap, int low, int high, int n)
{
if (cmap->codespace_len + 1 == nelem(cmap->codespace))
{
fz_warn("assert: too many code space ranges");
return;
}
 
cmap->codespace[cmap->codespace_len].n = n;
cmap->codespace[cmap->codespace_len].low = low;
cmap->codespace[cmap->codespace_len].high = high;
cmap->codespace_len ++;
}
 
/*
* Add an integer to the table.
*/
static void
add_table(pdf_cmap *cmap, int value)
{
if (cmap->tlen == USHRT_MAX)
{
fz_warn("cmap table is full; ignoring additional entries");
return;
}
if (cmap->tlen + 1 > cmap->tcap)
{
cmap->tcap = cmap->tcap > 1 ? (cmap->tcap * 3) / 2 : 256;
cmap->table = fz_realloc(cmap->table, cmap->tcap, sizeof(unsigned short));
}
cmap->table[cmap->tlen++] = value;
}
 
/*
* Add a range.
*/
static void
add_range(pdf_cmap *cmap, int low, int high, int flag, int offset)
{
/* If the range is too large to be represented, split it */
if (high - low > 0x3fff)
{
add_range(cmap, low, low+0x3fff, flag, offset);
add_range(cmap, low+0x3fff, high, flag, offset+0x3fff);
return;
}
if (cmap->rlen + 1 > cmap->rcap)
{
cmap->rcap = cmap->rcap > 1 ? (cmap->rcap * 3) / 2 : 256;
cmap->ranges = fz_realloc(cmap->ranges, cmap->rcap, sizeof(pdf_range));
}
cmap->ranges[cmap->rlen].low = low;
pdf_range_set_high(&cmap->ranges[cmap->rlen], high);
pdf_range_set_flags(&cmap->ranges[cmap->rlen], flag);
cmap->ranges[cmap->rlen].offset = offset;
cmap->rlen ++;
}
 
/*
* Add a range-to-table mapping.
*/
void
pdf_map_range_to_table(pdf_cmap *cmap, int low, int *table, int len)
{
int i;
int high = low + len;
int offset = cmap->tlen;
if (cmap->tlen + len >= USHRT_MAX)
fz_warn("cannot map range to table; table is full");
else
{
for (i = 0; i < len; i++)
add_table(cmap, table[i]);
add_range(cmap, low, high, PDF_CMAP_TABLE, offset);
}
}
 
/*
* Add a range of contiguous one-to-one mappings (ie 1..5 maps to 21..25)
*/
void
pdf_map_range_to_range(pdf_cmap *cmap, int low, int high, int offset)
{
add_range(cmap, low, high, high - low == 0 ? PDF_CMAP_SINGLE : PDF_CMAP_RANGE, offset);
}
 
/*
* Add a single one-to-many mapping.
*/
void
pdf_map_one_to_many(pdf_cmap *cmap, int low, int *values, int len)
{
int offset, i;
 
if (len == 1)
{
add_range(cmap, low, low, PDF_CMAP_SINGLE, values[0]);
return;
}
 
if (len > 8)
{
fz_warn("one to many mapping is too large (%d); truncating", len);
len = 8;
}
 
if (len == 2 &&
values[0] >= 0xD800 && values[0] <= 0xDBFF &&
values[1] >= 0xDC00 && values[1] <= 0xDFFF)
{
fz_warn("ignoring surrogate pair mapping in cmap");
return;
}
 
if (cmap->tlen + len + 1 >= USHRT_MAX)
fz_warn("cannot map one to many; table is full");
else
{
offset = cmap->tlen;
add_table(cmap, len);
for (i = 0; i < len; i++)
add_table(cmap, values[i]);
add_range(cmap, low, low, PDF_CMAP_MULTI, offset);
}
}
 
/*
* Sort the input ranges.
* Merge contiguous input ranges to range-to-range if the output is contiguous.
* Merge contiguous input ranges to range-to-table if the output is random.
*/
 
static int cmprange(const void *va, const void *vb)
{
return ((const pdf_range*)va)->low - ((const pdf_range*)vb)->low;
}
 
void
pdf_sort_cmap(pdf_cmap *cmap)
{
pdf_range *a; /* last written range on output */
pdf_range *b; /* current range examined on input */
 
if (cmap->rlen == 0)
return;
 
qsort(cmap->ranges, cmap->rlen, sizeof(pdf_range), cmprange);
 
if (cmap->tlen == USHRT_MAX)
{
fz_warn("cmap table is full; will not combine ranges");
return;
}
 
a = cmap->ranges;
b = cmap->ranges + 1;
 
while (b < cmap->ranges + cmap->rlen)
{
/* ignore one-to-many mappings */
if (pdf_range_flags(b) == PDF_CMAP_MULTI)
{
*(++a) = *b;
}
 
/* input contiguous */
else if (pdf_range_high(a) + 1 == b->low)
{
/* output contiguous */
if (pdf_range_high(a) - a->low + a->offset + 1 == b->offset)
{
/* SR -> R and SS -> R and RR -> R and RS -> R */
if ((pdf_range_flags(a) == PDF_CMAP_SINGLE || pdf_range_flags(a) == PDF_CMAP_RANGE) && (pdf_range_high(b) - a->low <= 0x3fff))
{
pdf_range_set_flags(a, PDF_CMAP_RANGE);
pdf_range_set_high(a, pdf_range_high(b));
}
 
/* LS -> L */
else if (pdf_range_flags(a) == PDF_CMAP_TABLE && pdf_range_flags(b) == PDF_CMAP_SINGLE && (pdf_range_high(b) - a->low <= 0x3fff))
{
pdf_range_set_high(a, pdf_range_high(b));
add_table(cmap, b->offset);
}
 
/* LR -> LR */
else if (pdf_range_flags(a) == PDF_CMAP_TABLE && pdf_range_flags(b) == PDF_CMAP_RANGE)
{
*(++a) = *b;
}
 
/* XX -> XX */
else
{
*(++a) = *b;
}
}
 
/* output separated */
else
{
/* SS -> L */
if (pdf_range_flags(a) == PDF_CMAP_SINGLE && pdf_range_flags(b) == PDF_CMAP_SINGLE)
{
pdf_range_set_flags(a, PDF_CMAP_TABLE);
pdf_range_set_high(a, pdf_range_high(b));
add_table(cmap, a->offset);
add_table(cmap, b->offset);
a->offset = cmap->tlen - 2;
}
 
/* LS -> L */
else if (pdf_range_flags(a) == PDF_CMAP_TABLE && pdf_range_flags(b) == PDF_CMAP_SINGLE && (pdf_range_high(b) - a->low <= 0x3fff))
{
pdf_range_set_high(a, pdf_range_high(b));
add_table(cmap, b->offset);
}
 
/* XX -> XX */
else
{
*(++a) = *b;
}
}
}
 
/* input separated: XX -> XX */
else
{
*(++a) = *b;
}
 
b ++;
}
 
cmap->rlen = a - cmap->ranges + 1;
 
fz_flush_warnings();
}
 
/*
* Lookup the mapping of a codepoint.
*/
int
pdf_lookup_cmap(pdf_cmap *cmap, int cpt)
{
int l = 0;
int r = cmap->rlen - 1;
int m;
 
while (l <= r)
{
m = (l + r) >> 1;
if (cpt < cmap->ranges[m].low)
r = m - 1;
else if (cpt > pdf_range_high(&cmap->ranges[m]))
l = m + 1;
else
{
int i = cpt - cmap->ranges[m].low + cmap->ranges[m].offset;
if (pdf_range_flags(&cmap->ranges[m]) == PDF_CMAP_TABLE)
return cmap->table[i];
if (pdf_range_flags(&cmap->ranges[m]) == PDF_CMAP_MULTI)
return -1; /* should use lookup_cmap_full */
return i;
}
}
 
if (cmap->usecmap)
return pdf_lookup_cmap(cmap->usecmap, cpt);
 
return -1;
}
 
int
pdf_lookup_cmap_full(pdf_cmap *cmap, int cpt, int *out)
{
int i, k, n;
int l = 0;
int r = cmap->rlen - 1;
int m;
 
while (l <= r)
{
m = (l + r) >> 1;
if (cpt < cmap->ranges[m].low)
r = m - 1;
else if (cpt > pdf_range_high(&cmap->ranges[m]))
l = m + 1;
else
{
k = cpt - cmap->ranges[m].low + cmap->ranges[m].offset;
if (pdf_range_flags(&cmap->ranges[m]) == PDF_CMAP_TABLE)
{
out[0] = cmap->table[k];
return 1;
}
else if (pdf_range_flags(&cmap->ranges[m]) == PDF_CMAP_MULTI)
{
n = cmap->ranges[m].offset;
for (i = 0; i < cmap->table[n]; i++)
out[i] = cmap->table[n + i + 1];
return cmap->table[n];
}
else
{
out[0] = k;
return 1;
}
}
}
 
if (cmap->usecmap)
return pdf_lookup_cmap_full(cmap->usecmap, cpt, out);
 
return 0;
}
 
/*
* Use the codespace ranges to extract a codepoint from a
* multi-byte encoded string.
*/
unsigned char *
pdf_decode_cmap(pdf_cmap *cmap, unsigned char *buf, int *cpt)
{
int k, n, c;
 
c = 0;
for (n = 0; n < 4; n++)
{
c = (c << 8) | buf[n];
for (k = 0; k < cmap->codespace_len; k++)
{
if (cmap->codespace[k].n == n + 1)
{
if (c >= cmap->codespace[k].low && c <= cmap->codespace[k].high)
{
*cpt = c;
return buf + n + 1;
}
}
}
}
 
*cpt = 0;
return buf + 1;
}
/contrib/media/updf/pdf/pdf_cmap_load.c
0,0 → 1,118
#include "fitz.h"
#include "mupdf.h"
 
/*
* Load CMap stream in PDF file
*/
fz_error
pdf_load_embedded_cmap(pdf_cmap **cmapp, pdf_xref *xref, fz_obj *stmobj)
{
fz_error error = fz_okay;
fz_stream *file = NULL;
pdf_cmap *cmap = NULL;
pdf_cmap *usecmap;
fz_obj *wmode;
fz_obj *obj;
 
if ((*cmapp = pdf_find_item(xref->store, pdf_drop_cmap, stmobj)))
{
pdf_keep_cmap(*cmapp);
return fz_okay;
}
 
error = pdf_open_stream(&file, xref, fz_to_num(stmobj), fz_to_gen(stmobj));
if (error)
{
error = fz_rethrow(error, "cannot open cmap stream (%d %d R)", fz_to_num(stmobj), fz_to_gen(stmobj));
goto cleanup;
}
 
error = pdf_parse_cmap(&cmap, file);
if (error)
{
error = fz_rethrow(error, "cannot parse cmap stream (%d %d R)", fz_to_num(stmobj), fz_to_gen(stmobj));
goto cleanup;
}
 
fz_close(file);
 
wmode = fz_dict_gets(stmobj, "WMode");
if (fz_is_int(wmode))
pdf_set_wmode(cmap, fz_to_int(wmode));
 
obj = fz_dict_gets(stmobj, "UseCMap");
if (fz_is_name(obj))
{
error = pdf_load_system_cmap(&usecmap, fz_to_name(obj));
if (error)
{
error = fz_rethrow(error, "cannot load system usecmap '%s'", fz_to_name(obj));
goto cleanup;
}
pdf_set_usecmap(cmap, usecmap);
pdf_drop_cmap(usecmap);
}
else if (fz_is_indirect(obj))
{
error = pdf_load_embedded_cmap(&usecmap, xref, obj);
if (error)
{
error = fz_rethrow(error, "cannot load embedded usecmap (%d %d R)", fz_to_num(obj), fz_to_gen(obj));
goto cleanup;
}
pdf_set_usecmap(cmap, usecmap);
pdf_drop_cmap(usecmap);
}
 
pdf_store_item(xref->store, pdf_keep_cmap, pdf_drop_cmap, stmobj, cmap);
 
*cmapp = cmap;
return fz_okay;
 
cleanup:
if (file)
fz_close(file);
if (cmap)
pdf_drop_cmap(cmap);
return error; /* already rethrown */
}
 
/*
* Create an Identity-* CMap (for both 1 and 2-byte encodings)
*/
pdf_cmap *
pdf_new_identity_cmap(int wmode, int bytes)
{
pdf_cmap *cmap = pdf_new_cmap();
sprintf(cmap->cmap_name, "Identity-%c", wmode ? 'V' : 'H');
pdf_add_codespace(cmap, 0x0000, 0xffff, bytes);
pdf_map_range_to_range(cmap, 0x0000, 0xffff, 0);
pdf_sort_cmap(cmap);
pdf_set_wmode(cmap, wmode);
return cmap;
}
 
/*
* Load predefined CMap from system.
*/
fz_error
pdf_load_system_cmap(pdf_cmap **cmapp, char *cmap_name)
{
pdf_cmap *usecmap;
pdf_cmap *cmap;
 
cmap = pdf_find_builtin_cmap(cmap_name);
if (!cmap)
return fz_throw("no builtin cmap file: %s", cmap_name);
 
if (cmap->usecmap_name[0] && !cmap->usecmap)
{
usecmap = pdf_find_builtin_cmap(cmap->usecmap_name);
if (!usecmap)
return fz_throw("nu builtin cmap file: %s", cmap->usecmap_name);
pdf_set_usecmap(cmap, usecmap);
}
 
*cmapp = cmap;
return fz_okay;
}
/contrib/media/updf/pdf/pdf_cmap_parse.c
0,0 → 1,490
#include "fitz.h"
#include "mupdf.h"
 
/*
* CMap parser
*/
 
enum
{
TOK_USECMAP = PDF_NUM_TOKENS,
TOK_BEGIN_CODESPACE_RANGE,
TOK_END_CODESPACE_RANGE,
TOK_BEGIN_BF_CHAR,
TOK_END_BF_CHAR,
TOK_BEGIN_BF_RANGE,
TOK_END_BF_RANGE,
TOK_BEGIN_CID_CHAR,
TOK_END_CID_CHAR,
TOK_BEGIN_CID_RANGE,
TOK_END_CID_RANGE,
TOK_END_CMAP
};
 
static int
pdf_cmap_token_from_keyword(char *key)
{
if (!strcmp(key, "usecmap")) return TOK_USECMAP;
if (!strcmp(key, "begincodespacerange")) return TOK_BEGIN_CODESPACE_RANGE;
if (!strcmp(key, "endcodespacerange")) return TOK_END_CODESPACE_RANGE;
if (!strcmp(key, "beginbfchar")) return TOK_BEGIN_BF_CHAR;
if (!strcmp(key, "endbfchar")) return TOK_END_BF_CHAR;
if (!strcmp(key, "beginbfrange")) return TOK_BEGIN_BF_RANGE;
if (!strcmp(key, "endbfrange")) return TOK_END_BF_RANGE;
if (!strcmp(key, "begincidchar")) return TOK_BEGIN_CID_CHAR;
if (!strcmp(key, "endcidchar")) return TOK_END_CID_CHAR;
if (!strcmp(key, "begincidrange")) return TOK_BEGIN_CID_RANGE;
if (!strcmp(key, "endcidrange")) return TOK_END_CID_RANGE;
if (!strcmp(key, "endcmap")) return TOK_END_CMAP;
return PDF_TOK_KEYWORD;
}
 
static int
pdf_code_from_string(char *buf, int len)
{
int a = 0;
while (len--)
a = (a << 8) | *(unsigned char *)buf++;
return a;
}
 
static fz_error
pdf_lex_cmap(int *tok, fz_stream *file, char *buf, int n, int *sl)
{
fz_error error;
 
error = pdf_lex(tok, file, buf, n, sl);
if (error)
return fz_rethrow(error, "cannot parse cmap token");
 
if (*tok == PDF_TOK_KEYWORD)
*tok = pdf_cmap_token_from_keyword(buf);
 
return fz_okay;
}
 
static fz_error
pdf_parse_cmap_name(pdf_cmap *cmap, fz_stream *file)
{
fz_error error;
char buf[256];
int tok;
int len;
 
error = pdf_lex_cmap(&tok, file, buf, sizeof buf, &len);
if (error)
return fz_rethrow(error, "syntaxerror in cmap");
 
if (tok == PDF_TOK_NAME)
fz_strlcpy(cmap->cmap_name, buf, sizeof(cmap->cmap_name));
else
fz_warn("expected name after CMapName in cmap");
 
return fz_okay;
}
 
static fz_error
pdf_parse_wmode(pdf_cmap *cmap, fz_stream *file)
{
fz_error error;
char buf[256];
int tok;
int len;
 
error = pdf_lex_cmap(&tok, file, buf, sizeof buf, &len);
if (error)
return fz_rethrow(error, "syntaxerror in cmap");
 
if (tok == PDF_TOK_INT)
pdf_set_wmode(cmap, atoi(buf));
else
fz_warn("expected integer after WMode in cmap");
 
return fz_okay;
}
 
static fz_error
pdf_parse_codespace_range(pdf_cmap *cmap, fz_stream *file)
{
fz_error error;
char buf[256];
int tok;
int len;
int lo, hi;
 
while (1)
{
error = pdf_lex_cmap(&tok, file, buf, sizeof buf, &len);
if (error)
return fz_rethrow(error, "syntaxerror in cmap");
 
if (tok == TOK_END_CODESPACE_RANGE)
return fz_okay;
 
else if (tok == PDF_TOK_STRING)
{
lo = pdf_code_from_string(buf, len);
error = pdf_lex_cmap(&tok, file, buf, sizeof buf, &len);
if (error)
return fz_rethrow(error, "syntaxerror in cmap");
if (tok == PDF_TOK_STRING)
{
hi = pdf_code_from_string(buf, len);
pdf_add_codespace(cmap, lo, hi, len);
}
else break;
}
 
else break;
}
 
return fz_throw("expected string or endcodespacerange");
}
 
static fz_error
pdf_parse_cid_range(pdf_cmap *cmap, fz_stream *file)
{
fz_error error;
char buf[256];
int tok;
int len;
int lo, hi, dst;
 
while (1)
{
error = pdf_lex_cmap(&tok, file, buf, sizeof buf, &len);
if (error)
return fz_rethrow(error, "syntaxerror in cmap");
 
if (tok == TOK_END_CID_RANGE)
return fz_okay;
 
else if (tok != PDF_TOK_STRING)
return fz_throw("expected string or endcidrange");
 
lo = pdf_code_from_string(buf, len);
 
error = pdf_lex_cmap(&tok, file, buf, sizeof buf, &len);
if (error)
return fz_rethrow(error, "syntaxerror in cmap");
if (tok != PDF_TOK_STRING)
return fz_throw("expected string");
 
hi = pdf_code_from_string(buf, len);
 
error = pdf_lex_cmap(&tok, file, buf, sizeof buf, &len);
if (error)
return fz_rethrow(error, "syntaxerror in cmap");
if (tok != PDF_TOK_INT)
return fz_throw("expected integer");
 
dst = atoi(buf);
 
pdf_map_range_to_range(cmap, lo, hi, dst);
}
}
 
static fz_error
pdf_parse_cid_char(pdf_cmap *cmap, fz_stream *file)
{
fz_error error;
char buf[256];
int tok;
int len;
int src, dst;
 
while (1)
{
error = pdf_lex_cmap(&tok, file, buf, sizeof buf, &len);
if (error)
return fz_rethrow(error, "syntaxerror in cmap");
 
if (tok == TOK_END_CID_CHAR)
return fz_okay;
 
else if (tok != PDF_TOK_STRING)
return fz_throw("expected string or endcidchar");
 
src = pdf_code_from_string(buf, len);
 
error = pdf_lex_cmap(&tok, file, buf, sizeof buf, &len);
if (error)
return fz_rethrow(error, "syntaxerror in cmap");
if (tok != PDF_TOK_INT)
return fz_throw("expected integer");
 
dst = atoi(buf);
 
pdf_map_range_to_range(cmap, src, src, dst);
}
}
 
static fz_error
pdf_parse_bf_range_array(pdf_cmap *cmap, fz_stream *file, int lo, int hi)
{
fz_error error;
char buf[256];
int tok;
int len;
int dst[256];
int i;
 
while (1)
{
error = pdf_lex_cmap(&tok, file, buf, sizeof buf, &len);
if (error)
return fz_rethrow(error, "syntaxerror in cmap");
 
if (tok == PDF_TOK_CLOSE_ARRAY)
return fz_okay;
 
/* Note: does not handle [ /Name /Name ... ] */
else if (tok != PDF_TOK_STRING)
return fz_throw("expected string or ]");
 
if (len / 2)
{
for (i = 0; i < len / 2; i++)
dst[i] = pdf_code_from_string(buf + i * 2, 2);
 
pdf_map_one_to_many(cmap, lo, dst, len / 2);
}
 
lo ++;
}
}
 
static fz_error
pdf_parse_bf_range(pdf_cmap *cmap, fz_stream *file)
{
fz_error error;
char buf[256];
int tok;
int len;
int lo, hi, dst;
 
while (1)
{
error = pdf_lex_cmap(&tok, file, buf, sizeof buf, &len);
if (error)
return fz_rethrow(error, "syntaxerror in cmap");
 
if (tok == TOK_END_BF_RANGE)
return fz_okay;
 
else if (tok != PDF_TOK_STRING)
return fz_throw("expected string or endbfrange");
 
lo = pdf_code_from_string(buf, len);
 
error = pdf_lex_cmap(&tok, file, buf, sizeof buf, &len);
if (error)
return fz_rethrow(error, "syntaxerror in cmap");
if (tok != PDF_TOK_STRING)
return fz_throw("expected string");
 
hi = pdf_code_from_string(buf, len);
 
error = pdf_lex_cmap(&tok, file, buf, sizeof buf, &len);
if (error)
return fz_rethrow(error, "syntaxerror in cmap");
 
if (tok == PDF_TOK_STRING)
{
if (len == 2)
{
dst = pdf_code_from_string(buf, len);
pdf_map_range_to_range(cmap, lo, hi, dst);
}
else
{
int dststr[256];
int i;
 
if (len / 2)
{
for (i = 0; i < len / 2; i++)
dststr[i] = pdf_code_from_string(buf + i * 2, 2);
 
while (lo <= hi)
{
dststr[i-1] ++;
pdf_map_one_to_many(cmap, lo, dststr, i);
lo ++;
}
}
}
}
 
else if (tok == PDF_TOK_OPEN_ARRAY)
{
error = pdf_parse_bf_range_array(cmap, file, lo, hi);
if (error)
return fz_rethrow(error, "cannot map bfrange");
}
 
else
{
return fz_throw("expected string or array or endbfrange");
}
}
}
 
static fz_error
pdf_parse_bf_char(pdf_cmap *cmap, fz_stream *file)
{
fz_error error;
char buf[256];
int tok;
int len;
int dst[256];
int src;
int i;
 
while (1)
{
error = pdf_lex_cmap(&tok, file, buf, sizeof buf, &len);
if (error)
return fz_rethrow(error, "syntaxerror in cmap");
 
if (tok == TOK_END_BF_CHAR)
return fz_okay;
 
else if (tok != PDF_TOK_STRING)
return fz_throw("expected string or endbfchar");
 
src = pdf_code_from_string(buf, len);
 
error = pdf_lex_cmap(&tok, file, buf, sizeof buf, &len);
if (error)
return fz_rethrow(error, "syntaxerror in cmap");
/* Note: does not handle /dstName */
if (tok != PDF_TOK_STRING)
return fz_throw("expected string");
 
if (len / 2)
{
for (i = 0; i < len / 2; i++)
dst[i] = pdf_code_from_string(buf + i * 2, 2);
pdf_map_one_to_many(cmap, src, dst, i);
}
}
}
 
fz_error
pdf_parse_cmap(pdf_cmap **cmapp, fz_stream *file)
{
fz_error error;
pdf_cmap *cmap;
char key[64];
char buf[256];
int tok;
int len;
 
cmap = pdf_new_cmap();
 
strcpy(key, ".notdef");
 
while (1)
{
error = pdf_lex_cmap(&tok, file, buf, sizeof buf, &len);
if (error)
{
error = fz_rethrow(error, "syntaxerror in cmap");
goto cleanup;
}
 
if (tok == PDF_TOK_EOF || tok == TOK_END_CMAP)
break;
 
else if (tok == PDF_TOK_NAME)
{
if (!strcmp(buf, "CMapName"))
{
error = pdf_parse_cmap_name(cmap, file);
if (error)
{
error = fz_rethrow(error, "syntaxerror in cmap after CMapName");
goto cleanup;
}
}
else if (!strcmp(buf, "WMode"))
{
error = pdf_parse_wmode(cmap, file);
if (error)
{
error = fz_rethrow(error, "syntaxerror in cmap after WMode");
goto cleanup;
}
}
else
fz_strlcpy(key, buf, sizeof key);
}
 
else if (tok == TOK_USECMAP)
{
fz_strlcpy(cmap->usecmap_name, key, sizeof(cmap->usecmap_name));
}
 
else if (tok == TOK_BEGIN_CODESPACE_RANGE)
{
error = pdf_parse_codespace_range(cmap, file);
if (error)
{
error = fz_rethrow(error, "syntaxerror in cmap codespacerange");
goto cleanup;
}
}
 
else if (tok == TOK_BEGIN_BF_CHAR)
{
error = pdf_parse_bf_char(cmap, file);
if (error)
{
error = fz_rethrow(error, "syntaxerror in cmap bfchar");
goto cleanup;
}
}
 
else if (tok == TOK_BEGIN_CID_CHAR)
{
error = pdf_parse_cid_char(cmap, file);
if (error)
{
error = fz_rethrow(error, "syntaxerror in cmap cidchar");
goto cleanup;
}
}
 
else if (tok == TOK_BEGIN_BF_RANGE)
{
error = pdf_parse_bf_range(cmap, file);
if (error)
{
error = fz_rethrow(error, "syntaxerror in cmap bfrange");
goto cleanup;
}
}
 
else if (tok == TOK_BEGIN_CID_RANGE)
{
error = pdf_parse_cid_range(cmap, file);
if (error)
{
error = fz_rethrow(error, "syntaxerror in cmap cidrange");
goto cleanup;
}
}
 
/* ignore everything else */
}
 
pdf_sort_cmap(cmap);
 
*cmapp = cmap;
return fz_okay;
 
cleanup:
pdf_drop_cmap(cmap);
return error; /* already rethrown */
}
/contrib/media/updf/pdf/pdf_cmap_table.c
0,0 → 1,184
#include "fitz.h"
#include "mupdf.h"
 
#ifndef NOCJK
#include "../generated/cmap_cns.h"
#include "../generated/cmap_gb.h"
#include "../generated/cmap_japan.h"
#include "../generated/cmap_korea.h"
#endif
 
static const struct { char *name; pdf_cmap *cmap; } cmap_table[] =
{
#ifndef NOCJK
{"78-EUC-H",&cmap_78_EUC_H},
{"78-EUC-V",&cmap_78_EUC_V},
{"78-H",&cmap_78_H},
{"78-RKSJ-H",&cmap_78_RKSJ_H},
{"78-RKSJ-V",&cmap_78_RKSJ_V},
{"78-V",&cmap_78_V},
{"78ms-RKSJ-H",&cmap_78ms_RKSJ_H},
{"78ms-RKSJ-V",&cmap_78ms_RKSJ_V},
{"83pv-RKSJ-H",&cmap_83pv_RKSJ_H},
{"90ms-RKSJ-H",&cmap_90ms_RKSJ_H},
{"90ms-RKSJ-V",&cmap_90ms_RKSJ_V},
{"90msp-RKSJ-H",&cmap_90msp_RKSJ_H},
{"90msp-RKSJ-V",&cmap_90msp_RKSJ_V},
{"90pv-RKSJ-H",&cmap_90pv_RKSJ_H},
{"90pv-RKSJ-V",&cmap_90pv_RKSJ_V},
{"Add-H",&cmap_Add_H},
{"Add-RKSJ-H",&cmap_Add_RKSJ_H},
{"Add-RKSJ-V",&cmap_Add_RKSJ_V},
{"Add-V",&cmap_Add_V},
{"Adobe-CNS1-0",&cmap_Adobe_CNS1_0},
{"Adobe-CNS1-1",&cmap_Adobe_CNS1_1},
{"Adobe-CNS1-2",&cmap_Adobe_CNS1_2},
{"Adobe-CNS1-3",&cmap_Adobe_CNS1_3},
{"Adobe-CNS1-4",&cmap_Adobe_CNS1_4},
{"Adobe-CNS1-5",&cmap_Adobe_CNS1_5},
{"Adobe-CNS1-6",&cmap_Adobe_CNS1_6},
{"Adobe-CNS1-UCS2",&cmap_Adobe_CNS1_UCS2},
{"Adobe-GB1-0",&cmap_Adobe_GB1_0},
{"Adobe-GB1-1",&cmap_Adobe_GB1_1},
{"Adobe-GB1-2",&cmap_Adobe_GB1_2},
{"Adobe-GB1-3",&cmap_Adobe_GB1_3},
{"Adobe-GB1-4",&cmap_Adobe_GB1_4},
{"Adobe-GB1-5",&cmap_Adobe_GB1_5},
{"Adobe-GB1-UCS2",&cmap_Adobe_GB1_UCS2},
{"Adobe-Japan1-0",&cmap_Adobe_Japan1_0},
{"Adobe-Japan1-1",&cmap_Adobe_Japan1_1},
{"Adobe-Japan1-2",&cmap_Adobe_Japan1_2},
{"Adobe-Japan1-3",&cmap_Adobe_Japan1_3},
{"Adobe-Japan1-4",&cmap_Adobe_Japan1_4},
{"Adobe-Japan1-5",&cmap_Adobe_Japan1_5},
{"Adobe-Japan1-6",&cmap_Adobe_Japan1_6},
{"Adobe-Japan1-UCS2",&cmap_Adobe_Japan1_UCS2},
{"Adobe-Japan2-0",&cmap_Adobe_Japan2_0},
{"Adobe-Korea1-0",&cmap_Adobe_Korea1_0},
{"Adobe-Korea1-1",&cmap_Adobe_Korea1_1},
{"Adobe-Korea1-2",&cmap_Adobe_Korea1_2},
{"Adobe-Korea1-UCS2",&cmap_Adobe_Korea1_UCS2},
{"B5-H",&cmap_B5_H},
{"B5-V",&cmap_B5_V},
{"B5pc-H",&cmap_B5pc_H},
{"B5pc-V",&cmap_B5pc_V},
{"CNS-EUC-H",&cmap_CNS_EUC_H},
{"CNS-EUC-V",&cmap_CNS_EUC_V},
{"CNS1-H",&cmap_CNS1_H},
{"CNS1-V",&cmap_CNS1_V},
{"CNS2-H",&cmap_CNS2_H},
{"CNS2-V",&cmap_CNS2_V},
{"ETHK-B5-H",&cmap_ETHK_B5_H},
{"ETHK-B5-V",&cmap_ETHK_B5_V},
{"ETen-B5-H",&cmap_ETen_B5_H},
{"ETen-B5-V",&cmap_ETen_B5_V},
{"ETenms-B5-H",&cmap_ETenms_B5_H},
{"ETenms-B5-V",&cmap_ETenms_B5_V},
{"EUC-H",&cmap_EUC_H},
{"EUC-V",&cmap_EUC_V},
{"Ext-H",&cmap_Ext_H},
{"Ext-RKSJ-H",&cmap_Ext_RKSJ_H},
{"Ext-RKSJ-V",&cmap_Ext_RKSJ_V},
{"Ext-V",&cmap_Ext_V},
{"GB-EUC-H",&cmap_GB_EUC_H},
{"GB-EUC-V",&cmap_GB_EUC_V},
{"GB-H",&cmap_GB_H},
{"GB-V",&cmap_GB_V},
{"GBK-EUC-H",&cmap_GBK_EUC_H},
{"GBK-EUC-V",&cmap_GBK_EUC_V},
{"GBK2K-H",&cmap_GBK2K_H},
{"GBK2K-V",&cmap_GBK2K_V},
{"GBKp-EUC-H",&cmap_GBKp_EUC_H},
{"GBKp-EUC-V",&cmap_GBKp_EUC_V},
{"GBT-EUC-H",&cmap_GBT_EUC_H},
{"GBT-EUC-V",&cmap_GBT_EUC_V},
{"GBT-H",&cmap_GBT_H},
{"GBT-V",&cmap_GBT_V},
{"GBTpc-EUC-H",&cmap_GBTpc_EUC_H},
{"GBTpc-EUC-V",&cmap_GBTpc_EUC_V},
{"GBpc-EUC-H",&cmap_GBpc_EUC_H},
{"GBpc-EUC-V",&cmap_GBpc_EUC_V},
{"H",&cmap_H},
{"HKdla-B5-H",&cmap_HKdla_B5_H},
{"HKdla-B5-V",&cmap_HKdla_B5_V},
{"HKdlb-B5-H",&cmap_HKdlb_B5_H},
{"HKdlb-B5-V",&cmap_HKdlb_B5_V},
{"HKgccs-B5-H",&cmap_HKgccs_B5_H},
{"HKgccs-B5-V",&cmap_HKgccs_B5_V},
{"HKm314-B5-H",&cmap_HKm314_B5_H},
{"HKm314-B5-V",&cmap_HKm314_B5_V},
{"HKm471-B5-H",&cmap_HKm471_B5_H},
{"HKm471-B5-V",&cmap_HKm471_B5_V},
{"HKscs-B5-H",&cmap_HKscs_B5_H},
{"HKscs-B5-V",&cmap_HKscs_B5_V},
{"Hankaku",&cmap_Hankaku},
{"Hiragana",&cmap_Hiragana},
{"Hojo-EUC-H",&cmap_Hojo_EUC_H},
{"Hojo-EUC-V",&cmap_Hojo_EUC_V},
{"Hojo-H",&cmap_Hojo_H},
{"Hojo-V",&cmap_Hojo_V},
{"KSC-EUC-H",&cmap_KSC_EUC_H},
{"KSC-EUC-V",&cmap_KSC_EUC_V},
{"KSC-H",&cmap_KSC_H},
{"KSC-Johab-H",&cmap_KSC_Johab_H},
{"KSC-Johab-V",&cmap_KSC_Johab_V},
{"KSC-V",&cmap_KSC_V},
{"KSCms-UHC-H",&cmap_KSCms_UHC_H},
{"KSCms-UHC-HW-H",&cmap_KSCms_UHC_HW_H},
{"KSCms-UHC-HW-V",&cmap_KSCms_UHC_HW_V},
{"KSCms-UHC-V",&cmap_KSCms_UHC_V},
{"KSCpc-EUC-H",&cmap_KSCpc_EUC_H},
{"KSCpc-EUC-V",&cmap_KSCpc_EUC_V},
{"Katakana",&cmap_Katakana},
{"NWP-H",&cmap_NWP_H},
{"NWP-V",&cmap_NWP_V},
{"RKSJ-H",&cmap_RKSJ_H},
{"RKSJ-V",&cmap_RKSJ_V},
{"Roman",&cmap_Roman},
{"UniCNS-UCS2-H",&cmap_UniCNS_UCS2_H},
{"UniCNS-UCS2-V",&cmap_UniCNS_UCS2_V},
{"UniCNS-UTF16-H",&cmap_UniCNS_UTF16_H},
{"UniCNS-UTF16-V",&cmap_UniCNS_UTF16_V},
{"UniGB-UCS2-H",&cmap_UniGB_UCS2_H},
{"UniGB-UCS2-V",&cmap_UniGB_UCS2_V},
{"UniGB-UTF16-H",&cmap_UniGB_UTF16_H},
{"UniGB-UTF16-V",&cmap_UniGB_UTF16_V},
{"UniHojo-UCS2-H",&cmap_UniHojo_UCS2_H},
{"UniHojo-UCS2-V",&cmap_UniHojo_UCS2_V},
{"UniHojo-UTF16-H",&cmap_UniHojo_UTF16_H},
{"UniHojo-UTF16-V",&cmap_UniHojo_UTF16_V},
{"UniJIS-UCS2-H",&cmap_UniJIS_UCS2_H},
{"UniJIS-UCS2-HW-H",&cmap_UniJIS_UCS2_HW_H},
{"UniJIS-UCS2-HW-V",&cmap_UniJIS_UCS2_HW_V},
{"UniJIS-UCS2-V",&cmap_UniJIS_UCS2_V},
{"UniJIS-UTF16-H",&cmap_UniJIS_UTF16_H},
{"UniJIS-UTF16-V",&cmap_UniJIS_UTF16_V},
{"UniJISPro-UCS2-HW-V",&cmap_UniJISPro_UCS2_HW_V},
{"UniJISPro-UCS2-V",&cmap_UniJISPro_UCS2_V},
{"UniKS-UCS2-H",&cmap_UniKS_UCS2_H},
{"UniKS-UCS2-V",&cmap_UniKS_UCS2_V},
{"UniKS-UTF16-H",&cmap_UniKS_UTF16_H},
{"UniKS-UTF16-V",&cmap_UniKS_UTF16_V},
{"V",&cmap_V},
{"WP-Symbol",&cmap_WP_Symbol},
#endif
};
 
pdf_cmap *
pdf_find_builtin_cmap(char *cmap_name)
{
int l = 0;
int r = nelem(cmap_table) - 1;
while (l <= r)
{
int m = (l + r) >> 1;
int c = strcmp(cmap_name, cmap_table[m].name);
if (c < 0)
r = m - 1;
else if (c > 0)
l = m + 1;
else
return cmap_table[m].cmap;
}
return NULL;
}
/contrib/media/updf/pdf/pdf_colorspace.c
0,0 → 1,387
#include "fitz.h"
#include "mupdf.h"
 
/* ICCBased */
 
static fz_error
load_icc_based(fz_colorspace **csp, pdf_xref *xref, fz_obj *dict)
{
int n;
 
n = fz_to_int(fz_dict_gets(dict, "N"));
 
switch (n)
{
case 1: *csp = fz_device_gray; return fz_okay;
case 3: *csp = fz_device_rgb; return fz_okay;
case 4: *csp = fz_device_cmyk; return fz_okay;
}
 
return fz_throw("syntaxerror: ICCBased must have 1, 3 or 4 components");
}
 
/* Lab */
 
static inline float fung(float x)
{
if (x >= 6.0f / 29.0f)
return x * x * x;
return (108.0f / 841.0f) * (x - (4.0f / 29.0f));
}
 
static void
lab_to_rgb(fz_colorspace *cs, float *lab, float *rgb)
{
/* input is in range (0..100, -128..127, -128..127) not (0..1, 0..1, 0..1) */
float lstar, astar, bstar, l, m, n, x, y, z, r, g, b;
lstar = lab[0];
astar = lab[1];
bstar = lab[2];
m = (lstar + 16) / 116;
l = m + astar / 500;
n = m - bstar / 200;
x = fung(l);
y = fung(m);
z = fung(n);
r = (3.240449f * x + -1.537136f * y + -0.498531f * z) * 0.830026f;
g = (-0.969265f * x + 1.876011f * y + 0.041556f * z) * 1.05452f;
b = (0.055643f * x + -0.204026f * y + 1.057229f * z) * 1.1003f;
rgb[0] = sqrtf(CLAMP(r, 0, 1));
rgb[1] = sqrtf(CLAMP(g, 0, 1));
rgb[2] = sqrtf(CLAMP(b, 0, 1));
}
 
static void
rgb_to_lab(fz_colorspace *cs, float *rgb, float *lab)
{
fz_warn("cannot convert into L*a*b colorspace");
lab[0] = rgb[0];
lab[1] = rgb[1];
lab[2] = rgb[2];
}
 
static fz_colorspace k_device_lab = { -1, "Lab", 3, lab_to_rgb, rgb_to_lab };
static fz_colorspace *fz_device_lab = &k_device_lab;
 
/* Separation and DeviceN */
 
struct separation
{
fz_colorspace *base;
pdf_function *tint;
};
 
static void
separation_to_rgb(fz_colorspace *cs, float *color, float *rgb)
{
struct separation *sep = cs->data;
float alt[FZ_MAX_COLORS];
pdf_eval_function(sep->tint, color, cs->n, alt, sep->base->n);
sep->base->to_rgb(sep->base, alt, rgb);
}
 
static void
free_separation(fz_colorspace *cs)
{
struct separation *sep = cs->data;
fz_drop_colorspace(sep->base);
pdf_drop_function(sep->tint);
fz_free(sep);
}
 
static fz_error
load_separation(fz_colorspace **csp, pdf_xref *xref, fz_obj *array)
{
fz_error error;
fz_colorspace *cs;
struct separation *sep;
fz_obj *nameobj = fz_array_get(array, 1);
fz_obj *baseobj = fz_array_get(array, 2);
fz_obj *tintobj = fz_array_get(array, 3);
fz_colorspace *base;
pdf_function *tint;
int n;
 
if (fz_is_array(nameobj))
n = fz_array_len(nameobj);
else
n = 1;
 
if (n > FZ_MAX_COLORS)
return fz_throw("too many components in colorspace");
 
error = pdf_load_colorspace(&base, xref, baseobj);
if (error)
return fz_rethrow(error, "cannot load base colorspace (%d %d R)", fz_to_num(baseobj), fz_to_gen(baseobj));
 
error = pdf_load_function(&tint, xref, tintobj);
if (error)
{
fz_drop_colorspace(base);
return fz_rethrow(error, "cannot load tint function (%d %d R)", fz_to_num(tintobj), fz_to_gen(tintobj));
}
 
sep = fz_malloc(sizeof(struct separation));
sep->base = base;
sep->tint = tint;
 
cs = fz_new_colorspace(n == 1 ? "Separation" : "DeviceN", n);
cs->to_rgb = separation_to_rgb;
cs->free_data = free_separation;
cs->data = sep;
 
*csp = cs;
return fz_okay;
}
 
/* Indexed */
 
struct indexed
{
fz_colorspace *base;
int high;
unsigned char *lookup;
};
 
static void
indexed_to_rgb(fz_colorspace *cs, float *color, float *rgb)
{
struct indexed *idx = cs->data;
float alt[FZ_MAX_COLORS];
int i, k;
i = color[0] * 255;
i = CLAMP(i, 0, idx->high);
for (k = 0; k < idx->base->n; k++)
alt[k] = idx->lookup[i * idx->base->n + k] / 255.0f;
idx->base->to_rgb(idx->base, alt, rgb);
}
 
static void
free_indexed(fz_colorspace *cs)
{
struct indexed *idx = cs->data;
if (idx->base)
fz_drop_colorspace(idx->base);
fz_free(idx->lookup);
fz_free(idx);
}
 
fz_pixmap *
pdf_expand_indexed_pixmap(fz_pixmap *src)
{
struct indexed *idx;
fz_pixmap *dst;
unsigned char *s, *d;
int y, x, k, n, high;
unsigned char *lookup;
 
assert(src->colorspace->to_rgb == indexed_to_rgb);
assert(src->n == 2);
 
idx = src->colorspace->data;
high = idx->high;
lookup = idx->lookup;
n = idx->base->n;
 
dst = fz_new_pixmap_with_rect(idx->base, fz_bound_pixmap(src));
s = src->samples;
d = dst->samples;
 
for (y = 0; y < src->h; y++)
{
for (x = 0; x < src->w; x++)
{
int v = *s++;
int a = *s++;
v = MIN(v, high);
for (k = 0; k < n; k++)
*d++ = fz_mul255(lookup[v * n + k], a);
*d++ = a;
}
}
 
if (src->mask)
dst->mask = fz_keep_pixmap(src->mask);
dst->interpolate = src->interpolate;
 
return dst;
}
 
static fz_error
load_indexed(fz_colorspace **csp, pdf_xref *xref, fz_obj *array)
{
fz_error error;
fz_colorspace *cs;
struct indexed *idx;
fz_obj *baseobj = fz_array_get(array, 1);
fz_obj *highobj = fz_array_get(array, 2);
fz_obj *lookup = fz_array_get(array, 3);
fz_colorspace *base;
int i, n;
 
error = pdf_load_colorspace(&base, xref, baseobj);
if (error)
return fz_rethrow(error, "cannot load base colorspace (%d %d R)", fz_to_num(baseobj), fz_to_gen(baseobj));
 
idx = fz_malloc(sizeof(struct indexed));
idx->base = base;
idx->high = fz_to_int(highobj);
idx->high = CLAMP(idx->high, 0, 255);
n = base->n * (idx->high + 1);
idx->lookup = fz_malloc(n);
memset(idx->lookup, 0, n);
 
cs = fz_new_colorspace("Indexed", 1);
cs->to_rgb = indexed_to_rgb;
cs->free_data = free_indexed;
cs->data = idx;
 
if (fz_is_string(lookup) && fz_to_str_len(lookup) == n)
{
unsigned char *buf = (unsigned char *) fz_to_str_buf(lookup);
for (i = 0; i < n; i++)
idx->lookup[i] = buf[i];
}
else if (fz_is_indirect(lookup))
{
fz_stream *file;
 
error = pdf_open_stream(&file, xref, fz_to_num(lookup), fz_to_gen(lookup));
if (error)
{
fz_drop_colorspace(cs);
return fz_rethrow(error, "cannot open colorspace lookup table (%d 0 R)", fz_to_num(lookup));
}
 
i = fz_read(file, idx->lookup, n);
if (i < 0)
{
fz_drop_colorspace(cs);
return fz_throw("cannot read colorspace lookup table (%d 0 R)", fz_to_num(lookup));
}
 
fz_close(file);
}
else
{
fz_drop_colorspace(cs);
return fz_throw("cannot parse colorspace lookup table");
}
 
*csp = cs;
return fz_okay;
}
 
/* Parse and create colorspace from PDF object */
 
static fz_error
pdf_load_colorspace_imp(fz_colorspace **csp, pdf_xref *xref, fz_obj *obj)
{
if (fz_is_name(obj))
{
if (!strcmp(fz_to_name(obj), "Pattern"))
*csp = fz_device_gray;
else if (!strcmp(fz_to_name(obj), "G"))
*csp = fz_device_gray;
else if (!strcmp(fz_to_name(obj), "RGB"))
*csp = fz_device_rgb;
else if (!strcmp(fz_to_name(obj), "CMYK"))
*csp = fz_device_cmyk;
else if (!strcmp(fz_to_name(obj), "DeviceGray"))
*csp = fz_device_gray;
else if (!strcmp(fz_to_name(obj), "DeviceRGB"))
*csp = fz_device_rgb;
else if (!strcmp(fz_to_name(obj), "DeviceCMYK"))
*csp = fz_device_cmyk;
else
return fz_throw("unknown colorspace: %s", fz_to_name(obj));
return fz_okay;
}
 
else if (fz_is_array(obj))
{
fz_obj *name = fz_array_get(obj, 0);
 
if (fz_is_name(name))
{
/* load base colorspace instead */
if (!strcmp(fz_to_name(name), "Pattern"))
{
fz_error error;
 
obj = fz_array_get(obj, 1);
if (!obj)
{
*csp = fz_device_gray;
return fz_okay;
}
 
error = pdf_load_colorspace(csp, xref, obj);
if (error)
return fz_rethrow(error, "cannot load pattern (%d %d R)", fz_to_num(obj), fz_to_gen(obj));
}
 
else if (!strcmp(fz_to_name(name), "G"))
*csp = fz_device_gray;
else if (!strcmp(fz_to_name(name), "RGB"))
*csp = fz_device_rgb;
else if (!strcmp(fz_to_name(name), "CMYK"))
*csp = fz_device_cmyk;
else if (!strcmp(fz_to_name(name), "DeviceGray"))
*csp = fz_device_gray;
else if (!strcmp(fz_to_name(name), "DeviceRGB"))
*csp = fz_device_rgb;
else if (!strcmp(fz_to_name(name), "DeviceCMYK"))
*csp = fz_device_cmyk;
else if (!strcmp(fz_to_name(name), "CalGray"))
*csp = fz_device_gray;
else if (!strcmp(fz_to_name(name), "CalRGB"))
*csp = fz_device_rgb;
else if (!strcmp(fz_to_name(name), "CalCMYK"))
*csp = fz_device_cmyk;
else if (!strcmp(fz_to_name(name), "Lab"))
*csp = fz_device_lab;
 
else if (!strcmp(fz_to_name(name), "ICCBased"))
return load_icc_based(csp, xref, fz_array_get(obj, 1));
 
else if (!strcmp(fz_to_name(name), "Indexed"))
return load_indexed(csp, xref, obj);
else if (!strcmp(fz_to_name(name), "I"))
return load_indexed(csp, xref, obj);
 
else if (!strcmp(fz_to_name(name), "Separation"))
return load_separation(csp, xref, obj);
 
else if (!strcmp(fz_to_name(name), "DeviceN"))
return load_separation(csp, xref, obj);
 
else
return fz_throw("syntaxerror: unknown colorspace %s", fz_to_name(name));
 
return fz_okay;
}
}
 
return fz_throw("syntaxerror: could not parse color space (%d %d R)", fz_to_num(obj), fz_to_gen(obj));
}
 
fz_error
pdf_load_colorspace(fz_colorspace **csp, pdf_xref *xref, fz_obj *obj)
{
fz_error error;
 
if ((*csp = pdf_find_item(xref->store, fz_drop_colorspace, obj)))
{
fz_keep_colorspace(*csp);
return fz_okay;
}
 
error = pdf_load_colorspace_imp(csp, xref, obj);
if (error)
return fz_rethrow(error, "cannot load colorspace (%d %d R)", fz_to_num(obj), fz_to_gen(obj));
 
pdf_store_item(xref->store, fz_keep_colorspace, fz_drop_colorspace, obj, *csp);
 
return fz_okay;
}
/contrib/media/updf/pdf/pdf_crypt.c
0,0 → 1,847
#include "fitz.h"
#include "mupdf.h"
 
enum
{
PDF_CRYPT_NONE,
PDF_CRYPT_RC4,
PDF_CRYPT_AESV2,
PDF_CRYPT_AESV3,
PDF_CRYPT_UNKNOWN,
};
 
typedef struct pdf_crypt_filter_s pdf_crypt_filter;
 
struct pdf_crypt_filter_s
{
int method;
int length;
};
 
struct pdf_crypt_s
{
fz_obj *id;
 
int v;
int length;
fz_obj *cf;
pdf_crypt_filter stmf;
pdf_crypt_filter strf;
 
int r;
unsigned char o[48];
unsigned char u[48];
unsigned char oe[32];
unsigned char ue[32];
int p;
int encrypt_metadata;
 
unsigned char key[32]; /* decryption key generated from password */
};
 
static fz_error pdf_parse_crypt_filter(pdf_crypt_filter *cf, fz_obj *dict, char *name, int defaultlength);
 
/*
* Create crypt object for decrypting strings and streams
* given the Encryption and ID objects.
*/
 
fz_error
pdf_new_crypt(pdf_crypt **cryptp, fz_obj *dict, fz_obj *id)
{
pdf_crypt *crypt;
fz_error error;
fz_obj *obj;
 
crypt = fz_malloc(sizeof(pdf_crypt));
memset(crypt, 0x00, sizeof(pdf_crypt));
 
/* Common to all security handlers (PDF 1.7 table 3.18) */
 
obj = fz_dict_gets(dict, "Filter");
if (!fz_is_name(obj))
{
pdf_free_crypt(crypt);
return fz_throw("unspecified encryption handler");
}
if (strcmp(fz_to_name(obj), "Standard") != 0)
{
pdf_free_crypt(crypt);
return fz_throw("unknown encryption handler: '%s'", fz_to_name(obj));
}
 
crypt->v = 0;
obj = fz_dict_gets(dict, "V");
if (fz_is_int(obj))
crypt->v = fz_to_int(obj);
if (crypt->v != 1 && crypt->v != 2 && crypt->v != 4 && crypt->v != 5)
{
pdf_free_crypt(crypt);
return fz_throw("unknown encryption version");
}
 
crypt->length = 40;
if (crypt->v == 2 || crypt->v == 4)
{
obj = fz_dict_gets(dict, "Length");
if (fz_is_int(obj))
crypt->length = fz_to_int(obj);
 
/* work-around for pdf generators that assume length is in bytes */
if (crypt->length < 40)
crypt->length = crypt->length * 8;
 
if (crypt->length % 8 != 0)
{
pdf_free_crypt(crypt);
return fz_throw("invalid encryption key length");
}
if (crypt->length > 256)
{
pdf_free_crypt(crypt);
return fz_throw("invalid encryption key length");
}
}
 
if (crypt->v == 5)
crypt->length = 256;
 
if (crypt->v == 1 || crypt->v == 2)
{
crypt->stmf.method = PDF_CRYPT_RC4;
crypt->stmf.length = crypt->length;
 
crypt->strf.method = PDF_CRYPT_RC4;
crypt->strf.length = crypt->length;
}
 
if (crypt->v == 4 || crypt->v == 5)
{
crypt->stmf.method = PDF_CRYPT_NONE;
crypt->stmf.length = crypt->length;
 
crypt->strf.method = PDF_CRYPT_NONE;
crypt->strf.length = crypt->length;
 
obj = fz_dict_gets(dict, "CF");
if (fz_is_dict(obj))
{
crypt->cf = fz_keep_obj(obj);
}
else
{
crypt->cf = NULL;
}
 
obj = fz_dict_gets(dict, "StmF");
if (fz_is_name(obj))
{
error = pdf_parse_crypt_filter(&crypt->stmf, crypt->cf, fz_to_name(obj), crypt->length);
if (error)
{
pdf_free_crypt(crypt);
return fz_rethrow(error, "cannot parse stream crypt filter (%d %d R)", fz_to_num(obj), fz_to_gen(obj));
}
}
 
obj = fz_dict_gets(dict, "StrF");
if (fz_is_name(obj))
{
error = pdf_parse_crypt_filter(&crypt->strf, crypt->cf, fz_to_name(obj), crypt->length);
if (error)
{
pdf_free_crypt(crypt);
return fz_rethrow(error, "cannot parse string crypt filter (%d %d R)", fz_to_num(obj), fz_to_gen(obj));
}
}
 
/* in crypt revision 4, the crypt filter determines the key length */
if (crypt->strf.method != PDF_CRYPT_NONE)
crypt->length = crypt->stmf.length;
}
 
/* Standard security handler (PDF 1.7 table 3.19) */
 
obj = fz_dict_gets(dict, "R");
if (fz_is_int(obj))
crypt->r = fz_to_int(obj);
else
{
pdf_free_crypt(crypt);
return fz_throw("encryption dictionary missing revision value");
}
 
obj = fz_dict_gets(dict, "O");
if (fz_is_string(obj) && fz_to_str_len(obj) == 32)
memcpy(crypt->o, fz_to_str_buf(obj), 32);
/* /O and /U are supposed to be 48 bytes long for revision 5, they're often longer, though */
else if (crypt->r == 5 && fz_is_string(obj) && fz_to_str_len(obj) >= 48)
memcpy(crypt->o, fz_to_str_buf(obj), 48);
else
{
pdf_free_crypt(crypt);
return fz_throw("encryption dictionary missing owner password");
}
 
obj = fz_dict_gets(dict, "U");
if (fz_is_string(obj) && fz_to_str_len(obj) == 32)
memcpy(crypt->u, fz_to_str_buf(obj), 32);
else if (fz_is_string(obj) && fz_to_str_len(obj) >= 48 && crypt->r == 5)
memcpy(crypt->u, fz_to_str_buf(obj), 48);
else if (fz_is_string(obj) && fz_to_str_len(obj) < 32)
{
fz_warn("encryption password key too short (%d)", fz_to_str_len(obj));
memcpy(crypt->u, fz_to_str_buf(obj), fz_to_str_len(obj));
}
else
{
pdf_free_crypt(crypt);
return fz_throw("encryption dictionary missing user password");
}
 
obj = fz_dict_gets(dict, "P");
if (fz_is_int(obj))
crypt->p = fz_to_int(obj);
else
{
pdf_free_crypt(crypt);
return fz_throw("encryption dictionary missing permissions value");
}
 
if (crypt->r == 5)
{
obj = fz_dict_gets(dict, "OE");
if (!fz_is_string(obj) || fz_to_str_len(obj) != 32)
{
pdf_free_crypt(crypt);
return fz_throw("encryption dictionary missing owner encryption key");
}
memcpy(crypt->oe, fz_to_str_buf(obj), 32);
 
obj = fz_dict_gets(dict, "UE");
if (!fz_is_string(obj) || fz_to_str_len(obj) != 32)
{
pdf_free_crypt(crypt);
return fz_throw("encryption dictionary missing user encryption key");
}
memcpy(crypt->ue, fz_to_str_buf(obj), 32);
}
 
crypt->encrypt_metadata = 1;
obj = fz_dict_gets(dict, "EncryptMetadata");
if (fz_is_bool(obj))
crypt->encrypt_metadata = fz_to_bool(obj);
 
/* Extract file identifier string */
 
if (fz_is_array(id) && fz_array_len(id) == 2)
{
obj = fz_array_get(id, 0);
if (fz_is_string(obj))
crypt->id = fz_keep_obj(obj);
}
else
fz_warn("missing file identifier, may not be able to do decryption");
 
*cryptp = crypt;
return fz_okay;
}
 
void
pdf_free_crypt(pdf_crypt *crypt)
{
if (crypt->id) fz_drop_obj(crypt->id);
if (crypt->cf) fz_drop_obj(crypt->cf);
fz_free(crypt);
}
 
/*
* Parse a CF dictionary entry (PDF 1.7 table 3.22)
*/
 
static fz_error
pdf_parse_crypt_filter(pdf_crypt_filter *cf, fz_obj *cf_obj, char *name, int defaultlength)
{
fz_obj *obj;
fz_obj *dict;
int is_identity = (strcmp(name, "Identity") == 0);
int is_stdcf = (!is_identity && (strcmp(name, "StdCF") == 0));
 
if (!is_identity && !is_stdcf)
{
return fz_throw("Crypt Filter not Identity or StdCF (%d %d R)", fz_to_num(cf_obj), fz_to_gen(cf_obj));
}
cf->method = PDF_CRYPT_NONE;
cf->length = defaultlength;
 
if (cf_obj == NULL)
{
cf->method = (is_identity ? PDF_CRYPT_NONE : PDF_CRYPT_RC4);
return fz_okay;
}
 
dict = fz_dict_gets(cf_obj, name);
if (!fz_is_dict(dict))
{
return fz_throw("cannot parse crypt filter (%d %d R)", fz_to_num(cf_obj), fz_to_gen(cf_obj));
}
obj = fz_dict_gets(dict, "CFM");
if (fz_is_name(obj))
{
if (!strcmp(fz_to_name(obj), "None"))
cf->method = PDF_CRYPT_NONE;
else if (!strcmp(fz_to_name(obj), "V2"))
cf->method = PDF_CRYPT_RC4;
else if (!strcmp(fz_to_name(obj), "AESV2"))
cf->method = PDF_CRYPT_AESV2;
else if (!strcmp(fz_to_name(obj), "AESV3"))
cf->method = PDF_CRYPT_AESV3;
else
fz_throw("unknown encryption method: %s", fz_to_name(obj));
}
 
obj = fz_dict_gets(dict, "Length");
if (fz_is_int(obj))
cf->length = fz_to_int(obj);
 
/* the length for crypt filters is supposed to be in bytes not bits */
if (cf->length < 40)
cf->length = cf->length * 8;
 
if ((cf->length % 8) != 0)
return fz_throw("invalid key length: %d", cf->length);
 
return fz_okay;
}
 
/*
* Compute an encryption key (PDF 1.7 algorithm 3.2)
*/
 
static const unsigned char padding[32] =
{
0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41,
0x64, 0x00, 0x4e, 0x56, 0xff, 0xfa, 0x01, 0x08,
0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80,
0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a
};
 
static void
pdf_compute_encryption_key(pdf_crypt *crypt, unsigned char *password, int pwlen, unsigned char *key)
{
unsigned char buf[32];
unsigned int p;
int i, n;
fz_md5 md5;
 
n = crypt->length / 8;
 
/* Step 1 - copy and pad password string */
if (pwlen > 32)
pwlen = 32;
memcpy(buf, password, pwlen);
memcpy(buf + pwlen, padding, 32 - pwlen);
 
/* Step 2 - init md5 and pass value of step 1 */
fz_md5_init(&md5);
fz_md5_update(&md5, buf, 32);
 
/* Step 3 - pass O value */
fz_md5_update(&md5, crypt->o, 32);
 
/* Step 4 - pass P value as unsigned int, low-order byte first */
p = (unsigned int) crypt->p;
buf[0] = (p) & 0xFF;
buf[1] = (p >> 8) & 0xFF;
buf[2] = (p >> 16) & 0xFF;
buf[3] = (p >> 24) & 0xFF;
fz_md5_update(&md5, buf, 4);
 
/* Step 5 - pass first element of ID array */
fz_md5_update(&md5, (unsigned char *)fz_to_str_buf(crypt->id), fz_to_str_len(crypt->id));
 
/* Step 6 (revision 4 or greater) - if metadata is not encrypted pass 0xFFFFFFFF */
if (crypt->r >= 4)
{
if (!crypt->encrypt_metadata)
{
buf[0] = 0xFF;
buf[1] = 0xFF;
buf[2] = 0xFF;
buf[3] = 0xFF;
fz_md5_update(&md5, buf, 4);
}
}
 
/* Step 7 - finish the hash */
fz_md5_final(&md5, buf);
 
/* Step 8 (revision 3 or greater) - do some voodoo 50 times */
if (crypt->r >= 3)
{
for (i = 0; i < 50; i++)
{
fz_md5_init(&md5);
fz_md5_update(&md5, buf, n);
fz_md5_final(&md5, buf);
}
}
 
/* Step 9 - the key is the first 'n' bytes of the result */
memcpy(key, buf, n);
}
 
/*
* Compute an encryption key (PDF 1.7 ExtensionLevel 3 algorithm 3.2a)
*/
 
static void
pdf_compute_encryption_key_r5(pdf_crypt *crypt, unsigned char *password, int pwlen, int ownerkey, unsigned char *validationkey)
{
unsigned char buffer[128 + 8 + 48];
fz_sha256 sha256;
fz_aes aes;
 
/* Step 2 - truncate UTF-8 password to 127 characters */
 
if (pwlen > 127)
pwlen = 127;
 
/* Step 3/4 - test password against owner/user key and compute encryption key */
 
memcpy(buffer, password, pwlen);
if (ownerkey)
{
memcpy(buffer + pwlen, crypt->o + 32, 8);
memcpy(buffer + pwlen + 8, crypt->u, 48);
}
else
memcpy(buffer + pwlen, crypt->u + 32, 8);
 
fz_sha256_init(&sha256);
fz_sha256_update(&sha256, buffer, pwlen + 8 + (ownerkey ? 48 : 0));
fz_sha256_final(&sha256, validationkey);
 
/* Step 3.5/4.5 - compute file encryption key from OE/UE */
 
memcpy(buffer + pwlen, crypt->u + 40, 8);
 
fz_sha256_init(&sha256);
fz_sha256_update(&sha256, buffer, pwlen + 8);
fz_sha256_final(&sha256, buffer);
 
// clear password buffer and use it as iv
memset(buffer + 32, 0, sizeof(buffer) - 32);
aes_setkey_dec(&aes, buffer, crypt->length);
aes_crypt_cbc(&aes, AES_DECRYPT, 32, buffer + 32, ownerkey ? crypt->oe : crypt->ue, crypt->key);
}
 
/*
* Computing the user password (PDF 1.7 algorithm 3.4 and 3.5)
* Also save the generated key for decrypting objects and streams in crypt->key.
*/
 
static void
pdf_compute_user_password(pdf_crypt *crypt, unsigned char *password, int pwlen, unsigned char *output)
{
if (crypt->r == 2)
{
fz_arc4 arc4;
 
pdf_compute_encryption_key(crypt, password, pwlen, crypt->key);
fz_arc4_init(&arc4, crypt->key, crypt->length / 8);
fz_arc4_encrypt(&arc4, output, padding, 32);
}
 
if (crypt->r == 3 || crypt->r == 4)
{
unsigned char xor[32];
unsigned char digest[16];
fz_md5 md5;
fz_arc4 arc4;
int i, x, n;
 
n = crypt->length / 8;
 
pdf_compute_encryption_key(crypt, password, pwlen, crypt->key);
 
fz_md5_init(&md5);
fz_md5_update(&md5, padding, 32);
fz_md5_update(&md5, (unsigned char*)fz_to_str_buf(crypt->id), fz_to_str_len(crypt->id));
fz_md5_final(&md5, digest);
 
fz_arc4_init(&arc4, crypt->key, n);
fz_arc4_encrypt(&arc4, output, digest, 16);
 
for (x = 1; x <= 19; x++)
{
for (i = 0; i < n; i++)
xor[i] = crypt->key[i] ^ x;
fz_arc4_init(&arc4, xor, n);
fz_arc4_encrypt(&arc4, output, output, 16);
}
 
memcpy(output + 16, padding, 16);
}
 
if (crypt->r == 5)
{
pdf_compute_encryption_key_r5(crypt, password, pwlen, 0, output);
}
}
 
/*
* Authenticating the user password (PDF 1.7 algorithm 3.6
* and ExtensionLevel 3 algorithm 3.11)
* This also has the side effect of saving a key generated
* from the password for decrypting objects and streams.
*/
 
static int
pdf_authenticate_user_password(pdf_crypt *crypt, unsigned char *password, int pwlen)
{
unsigned char output[32];
pdf_compute_user_password(crypt, password, pwlen, output);
if (crypt->r == 2 || crypt->r == 5)
return memcmp(output, crypt->u, 32) == 0;
if (crypt->r == 3 || crypt->r == 4)
return memcmp(output, crypt->u, 16) == 0;
return 0;
}
 
/*
* Authenticating the owner password (PDF 1.7 algorithm 3.7
* and ExtensionLevel 3 algorithm 3.12)
* Generates the user password from the owner password
* and calls pdf_authenticate_user_password.
*/
 
static int
pdf_authenticate_owner_password(pdf_crypt *crypt, unsigned char *ownerpass, int pwlen)
{
unsigned char pwbuf[32];
unsigned char key[32];
unsigned char xor[32];
unsigned char userpass[32];
int i, n, x;
fz_md5 md5;
fz_arc4 arc4;
 
if (crypt->r == 5)
{
/* PDF 1.7 ExtensionLevel 3 algorithm 3.12 */
 
pdf_compute_encryption_key_r5(crypt, ownerpass, pwlen, 1, key);
 
return !memcmp(key, crypt->o, 32);
}
 
n = crypt->length / 8;
 
/* Step 1 -- steps 1 to 4 of PDF 1.7 algorithm 3.3 */
 
/* copy and pad password string */
if (pwlen > 32)
pwlen = 32;
memcpy(pwbuf, ownerpass, pwlen);
memcpy(pwbuf + pwlen, padding, 32 - pwlen);
 
/* take md5 hash of padded password */
fz_md5_init(&md5);
fz_md5_update(&md5, pwbuf, 32);
fz_md5_final(&md5, key);
 
/* do some voodoo 50 times (Revision 3 or greater) */
if (crypt->r >= 3)
{
for (i = 0; i < 50; i++)
{
fz_md5_init(&md5);
fz_md5_update(&md5, key, 16);
fz_md5_final(&md5, key);
}
}
 
/* Step 2 (Revision 2) */
if (crypt->r == 2)
{
fz_arc4_init(&arc4, key, n);
fz_arc4_encrypt(&arc4, userpass, crypt->o, 32);
}
 
/* Step 2 (Revision 3 or greater) */
if (crypt->r >= 3)
{
memcpy(userpass, crypt->o, 32);
for (x = 0; x < 20; x++)
{
for (i = 0; i < n; i++)
xor[i] = key[i] ^ (19 - x);
fz_arc4_init(&arc4, xor, n);
fz_arc4_encrypt(&arc4, userpass, userpass, 32);
}
}
 
return pdf_authenticate_user_password(crypt, userpass, 32);
}
 
int
pdf_authenticate_password(pdf_xref *xref, char *password)
{
if (xref->crypt)
{
if (pdf_authenticate_user_password(xref->crypt, (unsigned char *)password, strlen(password)))
return 1;
if (pdf_authenticate_owner_password(xref->crypt, (unsigned char *)password, strlen(password)))
return 1;
return 0;
}
return 1;
}
 
int
pdf_needs_password(pdf_xref *xref)
{
if (!xref->crypt)
return 0;
if (pdf_authenticate_password(xref, ""))
return 0;
return 1;
}
 
int
pdf_has_permission(pdf_xref *xref, int p)
{
if (!xref->crypt)
return 1;
return xref->crypt->p & p;
}
 
unsigned char *
pdf_get_crypt_key(pdf_xref *xref)
{
if (xref->crypt)
return xref->crypt->key;
return NULL;
}
 
int
pdf_get_crypt_revision(pdf_xref *xref)
{
if (xref->crypt)
return xref->crypt->v;
return 0;
}
 
char *
pdf_get_crypt_method(pdf_xref *xref)
{
if (xref->crypt)
{
switch (xref->crypt->strf.method)
{
case PDF_CRYPT_NONE: return "None";
case PDF_CRYPT_RC4: return "RC4";
case PDF_CRYPT_AESV2: return "AES";
case PDF_CRYPT_AESV3: return "AES";
case PDF_CRYPT_UNKNOWN: return "Unknown";
}
}
return "None";
}
 
int
pdf_get_crypt_length(pdf_xref *xref)
{
if (xref->crypt)
return xref->crypt->length;
return 0;
}
 
/*
* PDF 1.7 algorithm 3.1 and ExtensionLevel 3 algorithm 3.1a
*
* Using the global encryption key that was generated from the
* password, create a new key that is used to decrypt indivual
* objects and streams. This key is based on the object and
* generation numbers.
*/
 
static int
pdf_compute_object_key(pdf_crypt *crypt, pdf_crypt_filter *cf, int num, int gen, unsigned char *key)
{
fz_md5 md5;
unsigned char message[5];
 
if (cf->method == PDF_CRYPT_AESV3)
{
memcpy(key, crypt->key, crypt->length / 8);
return crypt->length / 8;
}
 
fz_md5_init(&md5);
fz_md5_update(&md5, crypt->key, crypt->length / 8);
message[0] = (num) & 0xFF;
message[1] = (num >> 8) & 0xFF;
message[2] = (num >> 16) & 0xFF;
message[3] = (gen) & 0xFF;
message[4] = (gen >> 8) & 0xFF;
fz_md5_update(&md5, message, 5);
 
if (cf->method == PDF_CRYPT_AESV2)
fz_md5_update(&md5, (unsigned char *)"sAlT", 4);
 
fz_md5_final(&md5, key);
 
if (crypt->length / 8 + 5 > 16)
return 16;
return crypt->length / 8 + 5;
}
 
/*
* PDF 1.7 algorithm 3.1 and ExtensionLevel 3 algorithm 3.1a
*
* Decrypt all strings in obj modifying the data in-place.
* Recurse through arrays and dictionaries, but do not follow
* indirect references.
*/
 
static void
pdf_crypt_obj_imp(pdf_crypt *crypt, fz_obj *obj, unsigned char *key, int keylen)
{
unsigned char *s;
int i, n;
 
if (fz_is_indirect(obj))
return;
 
if (fz_is_string(obj))
{
s = (unsigned char *) fz_to_str_buf(obj);
n = fz_to_str_len(obj);
 
if (crypt->strf.method == PDF_CRYPT_RC4)
{
fz_arc4 arc4;
fz_arc4_init(&arc4, key, keylen);
fz_arc4_encrypt(&arc4, s, s, n);
}
 
if (crypt->strf.method == PDF_CRYPT_AESV2 || crypt->strf.method == PDF_CRYPT_AESV3)
{
if (n & 15 || n < 32)
fz_warn("invalid string length for aes encryption");
else
{
unsigned char iv[16];
fz_aes aes;
memcpy(iv, s, 16);
aes_setkey_dec(&aes, key, keylen * 8);
aes_crypt_cbc(&aes, AES_DECRYPT, n - 16, iv, s + 16, s);
/* delete space used for iv and padding bytes at end */
if (s[n - 17] < 1 || s[n - 17] > 16)
fz_warn("aes padding out of range");
else
fz_set_str_len(obj, n - 16 - s[n - 17]);
}
}
}
 
else if (fz_is_array(obj))
{
n = fz_array_len(obj);
for (i = 0; i < n; i++)
{
pdf_crypt_obj_imp(crypt, fz_array_get(obj, i), key, keylen);
}
}
 
else if (fz_is_dict(obj))
{
n = fz_dict_len(obj);
for (i = 0; i < n; i++)
{
pdf_crypt_obj_imp(crypt, fz_dict_get_val(obj, i), key, keylen);
}
}
}
 
void
pdf_crypt_obj(pdf_crypt *crypt, fz_obj *obj, int num, int gen)
{
unsigned char key[32];
int len;
 
len = pdf_compute_object_key(crypt, &crypt->strf, num, gen, key);
 
pdf_crypt_obj_imp(crypt, obj, key, len);
}
 
/*
* PDF 1.7 algorithm 3.1 and ExtensionLevel 3 algorithm 3.1a
*
* Create filter suitable for de/encrypting a stream.
*/
static fz_stream *
pdf_open_crypt_imp(fz_stream *chain, pdf_crypt *crypt, pdf_crypt_filter *stmf, int num, int gen)
{
unsigned char key[32];
int len;
 
len = pdf_compute_object_key(crypt, stmf, num, gen, key);
 
if (stmf->method == PDF_CRYPT_RC4)
return fz_open_arc4(chain, key, len);
 
if (stmf->method == PDF_CRYPT_AESV2 || stmf->method == PDF_CRYPT_AESV3)
return fz_open_aesd(chain, key, len);
 
return fz_open_copy(chain);
}
 
fz_stream *
pdf_open_crypt(fz_stream *chain, pdf_crypt *crypt, int num, int gen)
{
return pdf_open_crypt_imp(chain, crypt, &crypt->stmf, num, gen);
}
 
fz_stream *
pdf_open_crypt_with_filter(fz_stream *chain, pdf_crypt *crypt, char *name, int num, int gen)
{
fz_error error;
pdf_crypt_filter cf;
 
if (strcmp(name, "Identity"))
{
error = pdf_parse_crypt_filter(&cf, crypt->cf, name, crypt->length);
if (error)
fz_catch(error, "cannot parse crypt filter (%d %d R)", num, gen);
else
return pdf_open_crypt_imp(chain, crypt, &cf, num, gen);
}
return chain;
}
 
void pdf_debug_crypt(pdf_crypt *crypt)
{
int i;
 
printf("crypt {\n");
 
printf("\tv=%d length=%d\n", crypt->v, crypt->length);
printf("\tstmf method=%d length=%d\n", crypt->stmf.method, crypt->stmf.length);
printf("\tstrf method=%d length=%d\n", crypt->strf.method, crypt->strf.length);
printf("\tr=%d\n", crypt->r);
 
printf("\to=<");
for (i = 0; i < 32; i++)
printf("%02X", crypt->o[i]);
printf(">\n");
 
printf("\tu=<");
for (i = 0; i < 32; i++)
printf("%02X", crypt->u[i]);
printf(">\n");
 
printf("}\n");
}
/contrib/media/updf/pdf/pdf_encoding.c
0,0 → 1,83
#include "fitz.h"
#include "mupdf.h"
 
#include "data_encodings.h"
#include "data_glyphlist.h"
 
void
pdf_load_encoding(char **estrings, char *encoding)
{
char **bstrings = NULL;
int i;
 
if (!strcmp(encoding, "StandardEncoding"))
bstrings = (char**) pdf_standard;
if (!strcmp(encoding, "MacRomanEncoding"))
bstrings = (char**) pdf_mac_roman;
if (!strcmp(encoding, "MacExpertEncoding"))
bstrings = (char**) pdf_mac_expert;
if (!strcmp(encoding, "WinAnsiEncoding"))
bstrings = (char**) pdf_win_ansi;
 
if (bstrings)
for (i = 0; i < 256; i++)
estrings[i] = bstrings[i];
}
 
int
pdf_lookup_agl(char *name)
{
char buf[64];
char *p;
int l = 0;
int r = nelem(agl_name_list) - 1;
 
fz_strlcpy(buf, name, sizeof buf);
 
/* kill anything after first period and underscore */
p = strchr(buf, '.');
if (p) p[0] = 0;
p = strchr(buf, '_');
if (p) p[0] = 0;
 
while (l <= r)
{
int m = (l + r) >> 1;
int c = strcmp(buf, agl_name_list[m]);
if (c < 0)
r = m - 1;
else if (c > 0)
l = m + 1;
else
return agl_code_list[m];
}
 
if (strstr(buf, "uni") == buf)
return strtol(buf + 3, NULL, 16);
else if (strstr(buf, "u") == buf)
return strtol(buf + 1, NULL, 16);
else if (strstr(buf, "a") == buf && strlen(buf) >= 3)
return strtol(buf + 1, NULL, 10);
 
return 0;
}
 
static const char *empty_dup_list[] = { 0 };
 
const char **
pdf_lookup_agl_duplicates(int ucs)
{
int l = 0;
int r = nelem(agl_dup_offsets) / 2 - 1;
while (l <= r)
{
int m = (l + r) >> 1;
if (ucs < agl_dup_offsets[m << 1])
r = m - 1;
else if (ucs > agl_dup_offsets[m << 1])
l = m + 1;
else
return agl_dup_names + agl_dup_offsets[(m << 1) + 1];
}
return empty_dup_list;
}
/contrib/media/updf/pdf/pdf_font.c
0,0 → 1,1162
#include "fitz.h"
#include "mupdf.h"
 
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_XFREE86_H
 
static fz_error pdf_load_font_descriptor(pdf_font_desc *fontdesc, pdf_xref *xref, fz_obj *dict, char *collection, char *basefont);
 
static char *base_font_names[14][7] =
{
{ "Courier", "CourierNew", "CourierNewPSMT", NULL },
{ "Courier-Bold", "CourierNew,Bold", "Courier,Bold",
"CourierNewPS-BoldMT", "CourierNew-Bold", NULL },
{ "Courier-Oblique", "CourierNew,Italic", "Courier,Italic",
"CourierNewPS-ItalicMT", "CourierNew-Italic", NULL },
{ "Courier-BoldOblique", "CourierNew,BoldItalic", "Courier,BoldItalic",
"CourierNewPS-BoldItalicMT", "CourierNew-BoldItalic", NULL },
{ "Helvetica", "ArialMT", "Arial", NULL },
{ "Helvetica-Bold", "Arial-BoldMT", "Arial,Bold", "Arial-Bold",
"Helvetica,Bold", NULL },
{ "Helvetica-Oblique", "Arial-ItalicMT", "Arial,Italic", "Arial-Italic",
"Helvetica,Italic", "Helvetica-Italic", NULL },
{ "Helvetica-BoldOblique", "Arial-BoldItalicMT",
"Arial,BoldItalic", "Arial-BoldItalic",
"Helvetica,BoldItalic", "Helvetica-BoldItalic", NULL },
{ "Times-Roman", "TimesNewRomanPSMT", "TimesNewRoman",
"TimesNewRomanPS", NULL },
{ "Times-Bold", "TimesNewRomanPS-BoldMT", "TimesNewRoman,Bold",
"TimesNewRomanPS-Bold", "TimesNewRoman-Bold", NULL },
{ "Times-Italic", "TimesNewRomanPS-ItalicMT", "TimesNewRoman,Italic",
"TimesNewRomanPS-Italic", "TimesNewRoman-Italic", NULL },
{ "Times-BoldItalic", "TimesNewRomanPS-BoldItalicMT",
"TimesNewRoman,BoldItalic", "TimesNewRomanPS-BoldItalic",
"TimesNewRoman-BoldItalic", NULL },
{ "Symbol", NULL },
{ "ZapfDingbats", NULL }
};
 
static int is_dynalab(char *name)
{
if (strstr(name, "HuaTian"))
return 1;
if (strstr(name, "MingLi"))
return 1;
if ((strstr(name, "DF") == name) || strstr(name, "+DF"))
return 1;
if ((strstr(name, "DLC") == name) || strstr(name, "+DLC"))
return 1;
return 0;
}
 
static int strcmp_ignore_space(char *a, char *b)
{
while (1)
{
while (*a == ' ')
a++;
while (*b == ' ')
b++;
if (*a != *b)
return 1;
if (*a == 0)
return *a != *b;
if (*b == 0)
return *a != *b;
a++;
b++;
}
}
 
static char *clean_font_name(char *fontname)
{
int i, k;
for (i = 0; i < 14; i++)
for (k = 0; base_font_names[i][k]; k++)
if (!strcmp_ignore_space(base_font_names[i][k], fontname))
return base_font_names[i][0];
return fontname;
}
 
/*
* FreeType and Rendering glue
*/
 
enum { UNKNOWN, TYPE1, TRUETYPE };
 
static int ft_kind(FT_Face face)
{
const char *kind = FT_Get_X11_Font_Format(face);
if (!strcmp(kind, "TrueType"))
return TRUETYPE;
if (!strcmp(kind, "Type 1"))
return TYPE1;
if (!strcmp(kind, "CFF"))
return TYPE1;
if (!strcmp(kind, "CID Type 1"))
return TYPE1;
return UNKNOWN;
}
 
static int ft_is_bold(FT_Face face)
{
return face->style_flags & FT_STYLE_FLAG_BOLD;
}
 
static int ft_is_italic(FT_Face face)
{
return face->style_flags & FT_STYLE_FLAG_ITALIC;
}
 
static int ft_char_index(FT_Face face, int cid)
{
int gid = FT_Get_Char_Index(face, cid);
if (gid == 0)
gid = FT_Get_Char_Index(face, 0xf000 + cid);
 
/* some chinese fonts only ship the similarly looking 0x2026 */
if (gid == 0 && cid == 0x22ef)
gid = FT_Get_Char_Index(face, 0x2026);
 
return gid;
}
 
static int ft_cid_to_gid(pdf_font_desc *fontdesc, int cid)
{
if (fontdesc->to_ttf_cmap)
{
cid = pdf_lookup_cmap(fontdesc->to_ttf_cmap, cid);
return ft_char_index(fontdesc->font->ft_face, cid);
}
 
if (fontdesc->cid_to_gid)
return fontdesc->cid_to_gid[cid];
 
return cid;
}
 
int
pdf_font_cid_to_gid(pdf_font_desc *fontdesc, int cid)
{
if (fontdesc->font->ft_face)
return ft_cid_to_gid(fontdesc, cid);
return cid;
}
 
static int ft_width(pdf_font_desc *fontdesc, int cid)
{
int gid = ft_cid_to_gid(fontdesc, cid);
int fterr = FT_Load_Glyph(fontdesc->font->ft_face, gid,
FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM);
if (fterr)
{
fz_warn("freetype load glyph (gid %d): %s", gid, ft_error_string(fterr));
return 0;
}
return ((FT_Face)fontdesc->font->ft_face)->glyph->advance.x;
}
 
static int lookup_mre_code(char *name)
{
int i;
for (i = 0; i < 256; i++)
if (pdf_mac_roman[i] && !strcmp(name, pdf_mac_roman[i]))
return i;
return -1;
}
 
/*
* Load font files.
*/
 
static fz_error
pdf_load_builtin_font(pdf_font_desc *fontdesc, char *fontname)
{
fz_error error;
unsigned char *data;
unsigned int len;
 
data = pdf_find_builtin_font(fontname, &len);
if (!data)
return fz_throw("cannot find builtin font: '%s'", fontname);
 
error = fz_new_font_from_memory(&fontdesc->font, data, len, 0);
if (error)
return fz_rethrow(error, "cannot load freetype font from memory");
 
if (!strcmp(fontname, "Symbol") || !strcmp(fontname, "ZapfDingbats"))
fontdesc->flags |= PDF_FD_SYMBOLIC;
 
return fz_okay;
}
 
static fz_error
pdf_load_substitute_font(pdf_font_desc *fontdesc, int mono, int serif, int bold, int italic)
{
fz_error error;
unsigned char *data;
unsigned int len;
 
data = pdf_find_substitute_font(mono, serif, bold, italic, &len);
if (!data)
return fz_throw("cannot find substitute font");
 
error = fz_new_font_from_memory(&fontdesc->font, data, len, 0);
if (error)
return fz_rethrow(error, "cannot load freetype font from memory");
 
fontdesc->font->ft_substitute = 1;
fontdesc->font->ft_bold = bold && !ft_is_bold(fontdesc->font->ft_face);
fontdesc->font->ft_italic = italic && !ft_is_italic(fontdesc->font->ft_face);
return fz_okay;
}
 
static fz_error
pdf_load_substitute_cjk_font(pdf_font_desc *fontdesc, int ros, int serif)
{
fz_error error;
unsigned char *data;
unsigned int len;
 
data = pdf_find_substitute_cjk_font(ros, serif, &len);
if (!data)
return fz_throw("cannot find builtin CJK font");
 
error = fz_new_font_from_memory(&fontdesc->font, data, len, 0);
if (error)
return fz_rethrow(error, "cannot load builtin CJK font");
 
fontdesc->font->ft_substitute = 1;
return fz_okay;
}
 
static fz_error
pdf_load_system_font(pdf_font_desc *fontdesc, char *fontname, char *collection)
{
fz_error error;
int bold = 0;
int italic = 0;
int serif = 0;
int mono = 0;
 
if (strstr(fontname, "Bold"))
bold = 1;
if (strstr(fontname, "Italic"))
italic = 1;
if (strstr(fontname, "Oblique"))
italic = 1;
 
if (fontdesc->flags & PDF_FD_FIXED_PITCH)
mono = 1;
if (fontdesc->flags & PDF_FD_SERIF)
serif = 1;
if (fontdesc->flags & PDF_FD_ITALIC)
italic = 1;
if (fontdesc->flags & PDF_FD_FORCE_BOLD)
bold = 1;
 
if (collection)
{
if (!strcmp(collection, "Adobe-CNS1"))
return pdf_load_substitute_cjk_font(fontdesc, PDF_ROS_CNS, serif);
else if (!strcmp(collection, "Adobe-GB1"))
return pdf_load_substitute_cjk_font(fontdesc, PDF_ROS_GB, serif);
else if (!strcmp(collection, "Adobe-Japan1"))
return pdf_load_substitute_cjk_font(fontdesc, PDF_ROS_JAPAN, serif);
else if (!strcmp(collection, "Adobe-Korea1"))
return pdf_load_substitute_cjk_font(fontdesc, PDF_ROS_KOREA, serif);
return fz_throw("unknown cid collection: %s", collection);
}
 
error = pdf_load_substitute_font(fontdesc, mono, serif, bold, italic);
if (error)
return fz_rethrow(error, "cannot load substitute font");
 
return fz_okay;
}
 
static fz_error
pdf_load_embedded_font(pdf_font_desc *fontdesc, pdf_xref *xref, fz_obj *stmref)
{
fz_error error;
fz_buffer *buf;
 
error = pdf_load_stream(&buf, xref, fz_to_num(stmref), fz_to_gen(stmref));
if (error)
return fz_rethrow(error, "cannot load font stream (%d %d R)", fz_to_num(stmref), fz_to_gen(stmref));
 
error = fz_new_font_from_memory(&fontdesc->font, buf->data, buf->len, 0);
if (error)
{
fz_drop_buffer(buf);
return fz_rethrow(error, "cannot load embedded font (%d %d R)", fz_to_num(stmref), fz_to_gen(stmref));
}
 
/* save the buffer so we can free it later */
fontdesc->font->ft_data = buf->data;
fontdesc->font->ft_size = buf->len;
fz_free(buf); /* only free the fz_buffer struct, not the contained data */
 
fontdesc->is_embedded = 1;
 
return fz_okay;
}
 
/*
* Create and destroy
*/
 
pdf_font_desc *
pdf_keep_font(pdf_font_desc *fontdesc)
{
fontdesc->refs ++;
return fontdesc;
}
 
void
pdf_drop_font(pdf_font_desc *fontdesc)
{
if (fontdesc && --fontdesc->refs == 0)
{
if (fontdesc->font)
fz_drop_font(fontdesc->font);
if (fontdesc->encoding)
pdf_drop_cmap(fontdesc->encoding);
if (fontdesc->to_ttf_cmap)
pdf_drop_cmap(fontdesc->to_ttf_cmap);
if (fontdesc->to_unicode)
pdf_drop_cmap(fontdesc->to_unicode);
fz_free(fontdesc->cid_to_gid);
fz_free(fontdesc->cid_to_ucs);
fz_free(fontdesc->hmtx);
fz_free(fontdesc->vmtx);
fz_free(fontdesc);
}
}
 
pdf_font_desc *
pdf_new_font_desc(void)
{
pdf_font_desc *fontdesc;
 
fontdesc = fz_malloc(sizeof(pdf_font_desc));
fontdesc->refs = 1;
 
fontdesc->font = NULL;
 
fontdesc->flags = 0;
fontdesc->italic_angle = 0;
fontdesc->ascent = 0;
fontdesc->descent = 0;
fontdesc->cap_height = 0;
fontdesc->x_height = 0;
fontdesc->missing_width = 0;
 
fontdesc->encoding = NULL;
fontdesc->to_ttf_cmap = NULL;
fontdesc->cid_to_gid_len = 0;
fontdesc->cid_to_gid = NULL;
 
fontdesc->to_unicode = NULL;
fontdesc->cid_to_ucs_len = 0;
fontdesc->cid_to_ucs = NULL;
 
fontdesc->wmode = 0;
 
fontdesc->hmtx_cap = 0;
fontdesc->vmtx_cap = 0;
fontdesc->hmtx_len = 0;
fontdesc->vmtx_len = 0;
fontdesc->hmtx = NULL;
fontdesc->vmtx = NULL;
 
fontdesc->dhmtx.lo = 0x0000;
fontdesc->dhmtx.hi = 0xFFFF;
fontdesc->dhmtx.w = 1000;
 
fontdesc->dvmtx.lo = 0x0000;
fontdesc->dvmtx.hi = 0xFFFF;
fontdesc->dvmtx.x = 0;
fontdesc->dvmtx.y = 880;
fontdesc->dvmtx.w = -1000;
 
fontdesc->is_embedded = 0;
 
return fontdesc;
}
 
/*
* Simple fonts (Type1 and TrueType)
*/
 
static fz_error
pdf_load_simple_font(pdf_font_desc **fontdescp, pdf_xref *xref, fz_obj *dict)
{
fz_error error;
fz_obj *descriptor;
fz_obj *encoding;
fz_obj *widths;
unsigned short *etable = NULL;
pdf_font_desc *fontdesc;
FT_Face face;
FT_CharMap cmap;
int symbolic;
int kind;
 
char *basefont;
char *fontname;
char *estrings[256];
char ebuffer[256][32];
int i, k, n;
int fterr;
 
basefont = fz_to_name(fz_dict_gets(dict, "BaseFont"));
fontname = clean_font_name(basefont);
 
/* Load font file */
 
fontdesc = pdf_new_font_desc();
 
descriptor = fz_dict_gets(dict, "FontDescriptor");
if (descriptor)
error = pdf_load_font_descriptor(fontdesc, xref, descriptor, NULL, basefont);
else
error = pdf_load_builtin_font(fontdesc, fontname);
if (error)
goto cleanup;
 
/* Some chinese documents mistakenly consider WinAnsiEncoding to be codepage 936 */
if (!*fontdesc->font->name &&
!fz_dict_gets(dict, "ToUnicode") &&
!strcmp(fz_to_name(fz_dict_gets(dict, "Encoding")), "WinAnsiEncoding") &&
fz_to_int(fz_dict_gets(descriptor, "Flags")) == 4)
{
/* note: without the comma, pdf_load_font_descriptor would prefer /FontName over /BaseFont */
char *cp936fonts[] = {
"\xCB\xCE\xCC\xE5", "SimSun,Regular",
"\xBA\xDA\xCC\xE5", "SimHei,Regular",
"\xBF\xAC\xCC\xE5_GB2312", "SimKai,Regular",
"\xB7\xC2\xCB\xCE_GB2312", "SimFang,Regular",
"\xC1\xA5\xCA\xE9", "SimLi,Regular",
NULL
};
for (i = 0; cp936fonts[i]; i += 2)
if (!strcmp(basefont, cp936fonts[i]))
break;
if (cp936fonts[i])
{
fz_warn("workaround for S22PDF lying about chinese font encodings");
pdf_drop_font(fontdesc);
fontdesc = pdf_new_font_desc();
error = pdf_load_font_descriptor(fontdesc, xref, descriptor, "Adobe-GB1", cp936fonts[i+1]);
error |= pdf_load_system_cmap(&fontdesc->encoding, "GBK-EUC-H");
error |= pdf_load_system_cmap(&fontdesc->to_unicode, "Adobe-GB1-UCS2");
error |= pdf_load_system_cmap(&fontdesc->to_ttf_cmap, "Adobe-GB1-UCS2");
if (error)
return fz_rethrow(error, "cannot load font");
 
face = fontdesc->font->ft_face;
kind = ft_kind(face);
goto skip_encoding;
}
}
 
face = fontdesc->font->ft_face;
kind = ft_kind(face);
 
/* Encoding */
 
symbolic = fontdesc->flags & 4;
 
if (face->num_charmaps > 0)
cmap = face->charmaps[0];
else
cmap = NULL;
 
for (i = 0; i < face->num_charmaps; i++)
{
FT_CharMap test = face->charmaps[i];
 
if (kind == TYPE1)
{
if (test->platform_id == 7)
cmap = test;
}
 
if (kind == TRUETYPE)
{
if (test->platform_id == 1 && test->encoding_id == 0)
cmap = test;
if (test->platform_id == 3 && test->encoding_id == 1)
cmap = test;
}
}
 
if (cmap)
{
fterr = FT_Set_Charmap(face, cmap);
if (fterr)
fz_warn("freetype could not set cmap: %s", ft_error_string(fterr));
}
else
fz_warn("freetype could not find any cmaps");
 
etable = fz_calloc(256, sizeof(unsigned short));
for (i = 0; i < 256; i++)
{
estrings[i] = NULL;
etable[i] = 0;
}
 
encoding = fz_dict_gets(dict, "Encoding");
if (encoding)
{
if (fz_is_name(encoding))
pdf_load_encoding(estrings, fz_to_name(encoding));
 
if (fz_is_dict(encoding))
{
fz_obj *base, *diff, *item;
 
base = fz_dict_gets(encoding, "BaseEncoding");
if (fz_is_name(base))
pdf_load_encoding(estrings, fz_to_name(base));
else if (!fontdesc->is_embedded && !symbolic)
pdf_load_encoding(estrings, "StandardEncoding");
 
diff = fz_dict_gets(encoding, "Differences");
if (fz_is_array(diff))
{
n = fz_array_len(diff);
k = 0;
for (i = 0; i < n; i++)
{
item = fz_array_get(diff, i);
if (fz_is_int(item))
k = fz_to_int(item);
if (fz_is_name(item))
estrings[k++] = fz_to_name(item);
if (k < 0) k = 0;
if (k > 255) k = 255;
}
}
}
}
 
/* start with the builtin encoding */
for (i = 0; i < 256; i++)
etable[i] = ft_char_index(face, i);
 
/* encode by glyph name where we can */
if (kind == TYPE1)
{
for (i = 0; i < 256; i++)
{
if (estrings[i])
{
etable[i] = FT_Get_Name_Index(face, estrings[i]);
if (etable[i] == 0)
{
int aglcode = pdf_lookup_agl(estrings[i]);
const char **dupnames = pdf_lookup_agl_duplicates(aglcode);
while (*dupnames)
{
etable[i] = FT_Get_Name_Index(face, (char*)*dupnames);
if (etable[i])
break;
dupnames++;
}
}
}
}
}
 
/* encode by glyph name where we can */
if (kind == TRUETYPE)
{
/* Unicode cmap */
if (!symbolic && face->charmap && face->charmap->platform_id == 3)
{
for (i = 0; i < 256; i++)
{
if (estrings[i])
{
int aglcode = pdf_lookup_agl(estrings[i]);
if (!aglcode)
etable[i] = FT_Get_Name_Index(face, estrings[i]);
else
etable[i] = ft_char_index(face, aglcode);
}
}
}
 
/* MacRoman cmap */
else if (!symbolic && face->charmap && face->charmap->platform_id == 1)
{
for (i = 0; i < 256; i++)
{
if (estrings[i])
{
k = lookup_mre_code(estrings[i]);
if (k <= 0)
etable[i] = FT_Get_Name_Index(face, estrings[i]);
else
etable[i] = ft_char_index(face, k);
}
}
}
 
/* Symbolic cmap */
else
{
for (i = 0; i < 256; i++)
{
if (estrings[i])
{
etable[i] = FT_Get_Name_Index(face, estrings[i]);
if (etable[i] == 0)
etable[i] = ft_char_index(face, i);
}
}
}
}
 
/* try to reverse the glyph names from the builtin encoding */
for (i = 0; i < 256; i++)
{
if (etable[i] && !estrings[i])
{
if (FT_HAS_GLYPH_NAMES(face))
{
fterr = FT_Get_Glyph_Name(face, etable[i], ebuffer[i], 32);
if (fterr)
fz_warn("freetype get glyph name (gid %d): %s", etable[i], ft_error_string(fterr));
if (ebuffer[i][0])
estrings[i] = ebuffer[i];
}
else
{
estrings[i] = (char*) pdf_win_ansi[i]; /* discard const */
}
}
}
 
fontdesc->encoding = pdf_new_identity_cmap(0, 1);
fontdesc->cid_to_gid_len = 256;
fontdesc->cid_to_gid = etable;
 
error = pdf_load_to_unicode(fontdesc, xref, estrings, NULL, fz_dict_gets(dict, "ToUnicode"));
if (error)
fz_catch(error, "cannot load to_unicode");
 
skip_encoding:
 
/* Widths */
 
pdf_set_default_hmtx(fontdesc, fontdesc->missing_width);
 
widths = fz_dict_gets(dict, "Widths");
if (widths)
{
int first, last;
 
first = fz_to_int(fz_dict_gets(dict, "FirstChar"));
last = fz_to_int(fz_dict_gets(dict, "LastChar"));
 
if (first < 0 || last > 255 || first > last)
first = last = 0;
 
for (i = 0; i < last - first + 1; i++)
{
int wid = fz_to_int(fz_array_get(widths, i));
pdf_add_hmtx(fontdesc, i + first, i + first, wid);
}
}
else
{
fterr = FT_Set_Char_Size(face, 1000, 1000, 72, 72);
if (fterr)
fz_warn("freetype set character size: %s", ft_error_string(fterr));
for (i = 0; i < 256; i++)
{
pdf_add_hmtx(fontdesc, i, i, ft_width(fontdesc, i));
}
}
 
pdf_end_hmtx(fontdesc);
 
*fontdescp = fontdesc;
return fz_okay;
 
cleanup:
if (etable != fontdesc->cid_to_gid)
fz_free(etable);
pdf_drop_font(fontdesc);
return fz_rethrow(error, "cannot load simple font (%d %d R)", fz_to_num(dict), fz_to_gen(dict));
}
 
/*
* CID Fonts
*/
 
static fz_error
load_cid_font(pdf_font_desc **fontdescp, pdf_xref *xref, fz_obj *dict, fz_obj *encoding, fz_obj *to_unicode)
{
fz_error error;
fz_obj *widths;
fz_obj *descriptor;
pdf_font_desc *fontdesc;
FT_Face face;
int kind;
char collection[256];
char *basefont;
int i, k, fterr;
fz_obj *obj;
int dw;
 
/* Get font name and CID collection */
 
basefont = fz_to_name(fz_dict_gets(dict, "BaseFont"));
 
{
fz_obj *cidinfo;
char tmpstr[64];
int tmplen;
 
cidinfo = fz_dict_gets(dict, "CIDSystemInfo");
if (!cidinfo)
return fz_throw("cid font is missing info");
 
obj = fz_dict_gets(cidinfo, "Registry");
tmplen = MIN(sizeof tmpstr - 1, fz_to_str_len(obj));
memcpy(tmpstr, fz_to_str_buf(obj), tmplen);
tmpstr[tmplen] = '\0';
fz_strlcpy(collection, tmpstr, sizeof collection);
 
fz_strlcat(collection, "-", sizeof collection);
 
obj = fz_dict_gets(cidinfo, "Ordering");
tmplen = MIN(sizeof tmpstr - 1, fz_to_str_len(obj));
memcpy(tmpstr, fz_to_str_buf(obj), tmplen);
tmpstr[tmplen] = '\0';
fz_strlcat(collection, tmpstr, sizeof collection);
}
 
/* Load font file */
 
fontdesc = pdf_new_font_desc();
 
descriptor = fz_dict_gets(dict, "FontDescriptor");
if (descriptor)
error = pdf_load_font_descriptor(fontdesc, xref, descriptor, collection, basefont);
else
error = fz_throw("syntaxerror: missing font descriptor");
if (error)
goto cleanup;
 
face = fontdesc->font->ft_face;
kind = ft_kind(face);
 
/* Encoding */
 
error = fz_okay;
if (fz_is_name(encoding))
{
if (!strcmp(fz_to_name(encoding), "Identity-H"))
fontdesc->encoding = pdf_new_identity_cmap(0, 2);
else if (!strcmp(fz_to_name(encoding), "Identity-V"))
fontdesc->encoding = pdf_new_identity_cmap(1, 2);
else
error = pdf_load_system_cmap(&fontdesc->encoding, fz_to_name(encoding));
}
else if (fz_is_indirect(encoding))
{
error = pdf_load_embedded_cmap(&fontdesc->encoding, xref, encoding);
}
else
{
error = fz_throw("syntaxerror: font missing encoding");
}
if (error)
goto cleanup;
 
pdf_set_font_wmode(fontdesc, pdf_get_wmode(fontdesc->encoding));
 
if (kind == TRUETYPE)
{
fz_obj *cidtogidmap;
 
cidtogidmap = fz_dict_gets(dict, "CIDToGIDMap");
if (fz_is_indirect(cidtogidmap))
{
fz_buffer *buf;
 
error = pdf_load_stream(&buf, xref, fz_to_num(cidtogidmap), fz_to_gen(cidtogidmap));
if (error)
goto cleanup;
 
fontdesc->cid_to_gid_len = (buf->len) / 2;
fontdesc->cid_to_gid = fz_calloc(fontdesc->cid_to_gid_len, sizeof(unsigned short));
for (i = 0; i < fontdesc->cid_to_gid_len; i++)
fontdesc->cid_to_gid[i] = (buf->data[i * 2] << 8) + buf->data[i * 2 + 1];
 
fz_drop_buffer(buf);
}
 
/* if truetype font is external, cidtogidmap should not be identity */
/* so we map from cid to unicode and then map that through the (3 1) */
/* unicode cmap to get a glyph id */
else if (fontdesc->font->ft_substitute)
{
fterr = FT_Select_Charmap(face, ft_encoding_unicode);
if (fterr)
{
error = fz_throw("fonterror: no unicode cmap when emulating CID font: %s", ft_error_string(fterr));
goto cleanup;
}
 
if (!strcmp(collection, "Adobe-CNS1"))
error = pdf_load_system_cmap(&fontdesc->to_ttf_cmap, "Adobe-CNS1-UCS2");
else if (!strcmp(collection, "Adobe-GB1"))
error = pdf_load_system_cmap(&fontdesc->to_ttf_cmap, "Adobe-GB1-UCS2");
else if (!strcmp(collection, "Adobe-Japan1"))
error = pdf_load_system_cmap(&fontdesc->to_ttf_cmap, "Adobe-Japan1-UCS2");
else if (!strcmp(collection, "Adobe-Japan2"))
error = pdf_load_system_cmap(&fontdesc->to_ttf_cmap, "Adobe-Japan2-UCS2");
else if (!strcmp(collection, "Adobe-Korea1"))
error = pdf_load_system_cmap(&fontdesc->to_ttf_cmap, "Adobe-Korea1-UCS2");
else
error = fz_okay;
 
if (error)
{
error = fz_rethrow(error, "cannot load system cmap %s", collection);
goto cleanup;
}
}
}
 
error = pdf_load_to_unicode(fontdesc, xref, NULL, collection, to_unicode);
if (error)
fz_catch(error, "cannot load to_unicode");
 
/* Horizontal */
 
dw = 1000;
obj = fz_dict_gets(dict, "DW");
if (obj)
dw = fz_to_int(obj);
pdf_set_default_hmtx(fontdesc, dw);
 
widths = fz_dict_gets(dict, "W");
if (widths)
{
int c0, c1, w;
 
for (i = 0; i < fz_array_len(widths); )
{
c0 = fz_to_int(fz_array_get(widths, i));
obj = fz_array_get(widths, i + 1);
if (fz_is_array(obj))
{
for (k = 0; k < fz_array_len(obj); k++)
{
w = fz_to_int(fz_array_get(obj, k));
pdf_add_hmtx(fontdesc, c0 + k, c0 + k, w);
}
i += 2;
}
else
{
c1 = fz_to_int(obj);
w = fz_to_int(fz_array_get(widths, i + 2));
pdf_add_hmtx(fontdesc, c0, c1, w);
i += 3;
}
}
}
 
pdf_end_hmtx(fontdesc);
 
/* Vertical */
 
if (pdf_get_wmode(fontdesc->encoding) == 1)
{
int dw2y = 880;
int dw2w = -1000;
 
obj = fz_dict_gets(dict, "DW2");
if (obj)
{
dw2y = fz_to_int(fz_array_get(obj, 0));
dw2w = fz_to_int(fz_array_get(obj, 1));
}
 
pdf_set_default_vmtx(fontdesc, dw2y, dw2w);
 
widths = fz_dict_gets(dict, "W2");
if (widths)
{
int c0, c1, w, x, y;
 
for (i = 0; i < fz_array_len(widths); )
{
c0 = fz_to_int(fz_array_get(widths, i));
obj = fz_array_get(widths, i + 1);
if (fz_is_array(obj))
{
for (k = 0; k * 3 < fz_array_len(obj); k ++)
{
w = fz_to_int(fz_array_get(obj, k * 3 + 0));
x = fz_to_int(fz_array_get(obj, k * 3 + 1));
y = fz_to_int(fz_array_get(obj, k * 3 + 2));
pdf_add_vmtx(fontdesc, c0 + k, c0 + k, x, y, w);
}
i += 2;
}
else
{
c1 = fz_to_int(obj);
w = fz_to_int(fz_array_get(widths, i + 2));
x = fz_to_int(fz_array_get(widths, i + 3));
y = fz_to_int(fz_array_get(widths, i + 4));
pdf_add_vmtx(fontdesc, c0, c1, x, y, w);
i += 5;
}
}
}
 
pdf_end_vmtx(fontdesc);
}
 
*fontdescp = fontdesc;
return fz_okay;
 
cleanup:
pdf_drop_font(fontdesc);
return fz_rethrow(error, "cannot load cid font (%d %d R)", fz_to_num(dict), fz_to_gen(dict));
}
 
static fz_error
pdf_load_type0_font(pdf_font_desc **fontdescp, pdf_xref *xref, fz_obj *dict)
{
fz_error error;
fz_obj *dfonts;
fz_obj *dfont;
fz_obj *subtype;
fz_obj *encoding;
fz_obj *to_unicode;
 
dfonts = fz_dict_gets(dict, "DescendantFonts");
if (!dfonts)
return fz_throw("cid font is missing descendant fonts");
 
dfont = fz_array_get(dfonts, 0);
 
subtype = fz_dict_gets(dfont, "Subtype");
encoding = fz_dict_gets(dict, "Encoding");
to_unicode = fz_dict_gets(dict, "ToUnicode");
 
if (fz_is_name(subtype) && !strcmp(fz_to_name(subtype), "CIDFontType0"))
error = load_cid_font(fontdescp, xref, dfont, encoding, to_unicode);
else if (fz_is_name(subtype) && !strcmp(fz_to_name(subtype), "CIDFontType2"))
error = load_cid_font(fontdescp, xref, dfont, encoding, to_unicode);
else
error = fz_throw("syntaxerror: unknown cid font type");
if (error)
return fz_rethrow(error, "cannot load descendant font (%d %d R)", fz_to_num(dfont), fz_to_gen(dfont));
 
return fz_okay;
}
 
/*
* FontDescriptor
*/
 
static fz_error
pdf_load_font_descriptor(pdf_font_desc *fontdesc, pdf_xref *xref, fz_obj *dict, char *collection, char *basefont)
{
fz_error error;
fz_obj *obj1, *obj2, *obj3, *obj;
char *fontname;
char *origname;
FT_Face face;
 
if (!strchr(basefont, ',') || strchr(basefont, '+'))
origname = fz_to_name(fz_dict_gets(dict, "FontName"));
else
origname = basefont;
fontname = clean_font_name(origname);
 
fontdesc->flags = fz_to_int(fz_dict_gets(dict, "Flags"));
fontdesc->italic_angle = fz_to_real(fz_dict_gets(dict, "ItalicAngle"));
fontdesc->ascent = fz_to_real(fz_dict_gets(dict, "Ascent"));
fontdesc->descent = fz_to_real(fz_dict_gets(dict, "Descent"));
fontdesc->cap_height = fz_to_real(fz_dict_gets(dict, "CapHeight"));
fontdesc->x_height = fz_to_real(fz_dict_gets(dict, "XHeight"));
fontdesc->missing_width = fz_to_real(fz_dict_gets(dict, "MissingWidth"));
 
obj1 = fz_dict_gets(dict, "FontFile");
obj2 = fz_dict_gets(dict, "FontFile2");
obj3 = fz_dict_gets(dict, "FontFile3");
obj = obj1 ? obj1 : obj2 ? obj2 : obj3;
 
if (fz_is_indirect(obj))
{
error = pdf_load_embedded_font(fontdesc, xref, obj);
if (error)
{
fz_catch(error, "ignored error when loading embedded font, attempting to load system font");
if (origname != fontname)
error = pdf_load_builtin_font(fontdesc, fontname);
else
error = pdf_load_system_font(fontdesc, fontname, collection);
if (error)
return fz_rethrow(error, "cannot load font descriptor (%d %d R)", fz_to_num(dict), fz_to_gen(dict));
}
}
else
{
if (origname != fontname)
error = pdf_load_builtin_font(fontdesc, fontname);
else
error = pdf_load_system_font(fontdesc, fontname, collection);
if (error)
return fz_rethrow(error, "cannot load font descriptor (%d %d R)", fz_to_num(dict), fz_to_gen(dict));
}
 
fz_strlcpy(fontdesc->font->name, fontname, sizeof fontdesc->font->name);
 
/* Check for DynaLab fonts that must use hinting */
face = fontdesc->font->ft_face;
if (ft_kind(face) == TRUETYPE)
{
if (FT_IS_TRICKY(face) || is_dynalab(fontdesc->font->name))
fontdesc->font->ft_hint = 1;
}
 
return fz_okay;
 
}
 
static void
pdf_make_width_table(pdf_font_desc *fontdesc)
{
fz_font *font = fontdesc->font;
int i, k, cid, gid;
 
font->width_count = 0;
for (i = 0; i < fontdesc->hmtx_len; i++)
{
for (k = fontdesc->hmtx[i].lo; k <= fontdesc->hmtx[i].hi; k++)
{
cid = pdf_lookup_cmap(fontdesc->encoding, k);
gid = pdf_font_cid_to_gid(fontdesc, cid);
if (gid > font->width_count)
font->width_count = gid;
}
}
font->width_count ++;
 
font->width_table = fz_calloc(font->width_count, sizeof(int));
memset(font->width_table, 0, sizeof(int) * font->width_count);
 
for (i = 0; i < fontdesc->hmtx_len; i++)
{
for (k = fontdesc->hmtx[i].lo; k <= fontdesc->hmtx[i].hi; k++)
{
cid = pdf_lookup_cmap(fontdesc->encoding, k);
gid = pdf_font_cid_to_gid(fontdesc, cid);
if (gid >= 0 && gid < font->width_count)
font->width_table[gid] = fontdesc->hmtx[i].w;
}
}
}
 
fz_error
pdf_load_font(pdf_font_desc **fontdescp, pdf_xref *xref, fz_obj *rdb, fz_obj *dict)
{
fz_error error;
char *subtype;
fz_obj *dfonts;
fz_obj *charprocs;
 
if ((*fontdescp = pdf_find_item(xref->store, pdf_drop_font, dict)))
{
pdf_keep_font(*fontdescp);
return fz_okay;
}
 
subtype = fz_to_name(fz_dict_gets(dict, "Subtype"));
dfonts = fz_dict_gets(dict, "DescendantFonts");
charprocs = fz_dict_gets(dict, "CharProcs");
 
if (subtype && !strcmp(subtype, "Type0"))
error = pdf_load_type0_font(fontdescp, xref, dict);
else if (subtype && !strcmp(subtype, "Type1"))
error = pdf_load_simple_font(fontdescp, xref, dict);
else if (subtype && !strcmp(subtype, "MMType1"))
error = pdf_load_simple_font(fontdescp, xref, dict);
else if (subtype && !strcmp(subtype, "TrueType"))
error = pdf_load_simple_font(fontdescp, xref, dict);
else if (subtype && !strcmp(subtype, "Type3"))
error = pdf_load_type3_font(fontdescp, xref, rdb, dict);
else if (charprocs)
{
fz_warn("unknown font format, guessing type3.");
error = pdf_load_type3_font(fontdescp, xref, rdb, dict);
}
else if (dfonts)
{
fz_warn("unknown font format, guessing type0.");
error = pdf_load_type0_font(fontdescp, xref, dict);
}
else
{
fz_warn("unknown font format, guessing type1 or truetype.");
error = pdf_load_simple_font(fontdescp, xref, dict);
}
if (error)
return fz_rethrow(error, "cannot load font (%d %d R)", fz_to_num(dict), fz_to_gen(dict));
 
/* Save the widths to stretch non-CJK substitute fonts */
if ((*fontdescp)->font->ft_substitute && !(*fontdescp)->to_ttf_cmap)
pdf_make_width_table(*fontdescp);
 
pdf_store_item(xref->store, pdf_keep_font, pdf_drop_font, dict, *fontdescp);
 
return fz_okay;
}
 
void
pdf_debug_font(pdf_font_desc *fontdesc)
{
int i;
 
printf("fontdesc {\n");
 
if (fontdesc->font->ft_face)
printf("\tfreetype font\n");
if (fontdesc->font->t3procs)
printf("\ttype3 font\n");
 
printf("\twmode %d\n", fontdesc->wmode);
printf("\tDW %d\n", fontdesc->dhmtx.w);
 
printf("\tW {\n");
for (i = 0; i < fontdesc->hmtx_len; i++)
printf("\t\t<%04x> <%04x> %d\n",
fontdesc->hmtx[i].lo, fontdesc->hmtx[i].hi, fontdesc->hmtx[i].w);
printf("\t}\n");
 
if (fontdesc->wmode)
{
printf("\tDW2 [%d %d]\n", fontdesc->dvmtx.y, fontdesc->dvmtx.w);
printf("\tW2 {\n");
for (i = 0; i < fontdesc->vmtx_len; i++)
printf("\t\t<%04x> <%04x> %d %d %d\n", fontdesc->vmtx[i].lo, fontdesc->vmtx[i].hi,
fontdesc->vmtx[i].x, fontdesc->vmtx[i].y, fontdesc->vmtx[i].w);
printf("\t}\n");
}
}
/contrib/media/updf/pdf/pdf_fontfile.c
0,0 → 1,139
#include "fitz.h"
#include "mupdf.h"
 
#ifdef NOCJK
#define NOCJKFONT
#endif
 
#include "../generated/font_base14.h"
 
#ifndef NODROIDFONT
#include "../generated/font_droid.h"
#endif
 
#ifndef NOCJKFONT
#include "../generated/font_cjk.h"
#endif
 
unsigned char *
pdf_find_builtin_font(char *name, unsigned int *len)
{
if (!strcmp("Courier", name)) {
*len = sizeof pdf_font_NimbusMonL_Regu;
return (unsigned char*) pdf_font_NimbusMonL_Regu;
}
if (!strcmp("Courier-Bold", name)) {
*len = sizeof pdf_font_NimbusMonL_Bold;
return (unsigned char*) pdf_font_NimbusMonL_Bold;
}
if (!strcmp("Courier", name)) {
*len = sizeof pdf_font_NimbusMonL_Regu;
return (unsigned char*) pdf_font_NimbusMonL_Regu;
}
if (!strcmp("Courier-Bold", name)) {
*len = sizeof pdf_font_NimbusMonL_Bold;
return (unsigned char*) pdf_font_NimbusMonL_Bold;
}
if (!strcmp("Courier-Oblique", name)) {
*len = sizeof pdf_font_NimbusMonL_ReguObli;
return (unsigned char*) pdf_font_NimbusMonL_ReguObli;
}
if (!strcmp("Courier-BoldOblique", name)) {
*len = sizeof pdf_font_NimbusMonL_BoldObli;
return (unsigned char*) pdf_font_NimbusMonL_BoldObli;
}
if (!strcmp("Helvetica", name)) {
*len = sizeof pdf_font_NimbusSanL_Regu;
return (unsigned char*) pdf_font_NimbusSanL_Regu;
}
if (!strcmp("Helvetica-Bold", name)) {
*len = sizeof pdf_font_NimbusSanL_Bold;
return (unsigned char*) pdf_font_NimbusSanL_Bold;
}
if (!strcmp("Helvetica-Oblique", name)) {
*len = sizeof pdf_font_NimbusSanL_ReguItal;
return (unsigned char*) pdf_font_NimbusSanL_ReguItal;
}
if (!strcmp("Helvetica-BoldOblique", name)) {
*len = sizeof pdf_font_NimbusSanL_BoldItal;
return (unsigned char*) pdf_font_NimbusSanL_BoldItal;
}
if (!strcmp("Times-Roman", name)) {
*len = sizeof pdf_font_NimbusRomNo9L_Regu;
return (unsigned char*) pdf_font_NimbusRomNo9L_Regu;
}
if (!strcmp("Times-Bold", name)) {
*len = sizeof pdf_font_NimbusRomNo9L_Medi;
return (unsigned char*) pdf_font_NimbusRomNo9L_Medi;
}
if (!strcmp("Times-Italic", name)) {
*len = sizeof pdf_font_NimbusRomNo9L_ReguItal;
return (unsigned char*) pdf_font_NimbusRomNo9L_ReguItal;
}
if (!strcmp("Times-BoldItalic", name)) {
*len = sizeof pdf_font_NimbusRomNo9L_MediItal;
return (unsigned char*) pdf_font_NimbusRomNo9L_MediItal;
}
if (!strcmp("Symbol", name)) {
*len = sizeof pdf_font_StandardSymL;
return (unsigned char*) pdf_font_StandardSymL;
}
if (!strcmp("ZapfDingbats", name)) {
*len = sizeof pdf_font_Dingbats;
return (unsigned char*) pdf_font_Dingbats;
}
*len = 0;
return NULL;
}
 
unsigned char *
pdf_find_substitute_font(int mono, int serif, int bold, int italic, unsigned int *len)
{
#ifdef NODROIDFONT
if (mono) {
if (bold) {
if (italic) return pdf_find_builtin_font("Courier-BoldOblique", len);
else return pdf_find_builtin_font("Courier-Bold", len);
} else {
if (italic) return pdf_find_builtin_font("Courier-Oblique", len);
else return pdf_find_builtin_font("Courier", len);
}
} else if (serif) {
if (bold) {
if (italic) return pdf_find_builtin_font("Times-BoldItalic", len);
else return pdf_find_builtin_font("Times-Bold", len);
} else {
if (italic) return pdf_find_builtin_font("Times-Italic", len);
else return pdf_find_builtin_font("Times-Roman", len);
}
} else {
if (bold) {
if (italic) return pdf_find_builtin_font("Helvetica-BoldOblique", len);
else return pdf_find_builtin_font("Helvetica-Bold", len);
} else {
if (italic) return pdf_find_builtin_font("Helvetica-Oblique", len);
else return pdf_find_builtin_font("Helvetica", len);
}
}
#else
if (mono) {
*len = sizeof pdf_font_DroidSansMono;
return (unsigned char*) pdf_font_DroidSansMono;
} else {
*len = sizeof pdf_font_DroidSans;
return (unsigned char*) pdf_font_DroidSans;
}
#endif
}
 
unsigned char *
pdf_find_substitute_cjk_font(int ros, int serif, unsigned int *len)
{
#ifndef NOCJKFONT
*len = sizeof pdf_font_DroidSansFallback;
return (unsigned char*) pdf_font_DroidSansFallback;
#else
*len = 0;
return NULL;
#endif
}
/contrib/media/updf/pdf/pdf_function.c
0,0 → 1,1694
#include "fitz.h"
#include "mupdf.h"
 
enum
{
MAXN = FZ_MAX_COLORS,
MAXM = FZ_MAX_COLORS,
};
 
typedef struct psobj_s psobj;
 
enum
{
SAMPLE = 0,
EXPONENTIAL = 2,
STITCHING = 3,
POSTSCRIPT = 4
};
 
struct pdf_function_s
{
int refs;
int type; /* 0=sample 2=exponential 3=stitching 4=postscript */
int m; /* number of input values */
int n; /* number of output values */
float domain[MAXM][2]; /* even index : min value, odd index : max value */
float range[MAXN][2]; /* even index : min value, odd index : max value */
int has_range;
 
union
{
struct {
unsigned short bps;
int size[MAXM];
float encode[MAXM][2];
float decode[MAXN][2];
float *samples;
} sa;
 
struct {
float n;
float c0[MAXN];
float c1[MAXN];
} e;
 
struct {
int k;
pdf_function **funcs; /* k */
float *bounds; /* k - 1 */
float *encode; /* k * 2 */
} st;
 
struct {
psobj *code;
int cap;
} p;
} u;
};
 
#define RADIAN 57.2957795
 
static inline float lerp(float x, float xmin, float xmax, float ymin, float ymax)
{
if (xmin == xmax)
return ymin;
if (ymin == ymax)
return ymin;
return ymin + (x - xmin) * (ymax - ymin) / (xmax - xmin);
}
 
/*
* PostScript calculator
*/
 
enum { PS_BOOL, PS_INT, PS_REAL, PS_OPERATOR, PS_BLOCK };
 
enum
{
PS_OP_ABS, PS_OP_ADD, PS_OP_AND, PS_OP_ATAN, PS_OP_BITSHIFT,
PS_OP_CEILING, PS_OP_COPY, PS_OP_COS, PS_OP_CVI, PS_OP_CVR,
PS_OP_DIV, PS_OP_DUP, PS_OP_EQ, PS_OP_EXCH, PS_OP_EXP,
PS_OP_FALSE, PS_OP_FLOOR, PS_OP_GE, PS_OP_GT, PS_OP_IDIV,
PS_OP_INDEX, PS_OP_LE, PS_OP_LN, PS_OP_LOG, PS_OP_LT, PS_OP_MOD,
PS_OP_MUL, PS_OP_NE, PS_OP_NEG, PS_OP_NOT, PS_OP_OR, PS_OP_POP,
PS_OP_ROLL, PS_OP_ROUND, PS_OP_SIN, PS_OP_SQRT, PS_OP_SUB,
PS_OP_TRUE, PS_OP_TRUNCATE, PS_OP_XOR, PS_OP_IF, PS_OP_IFELSE,
PS_OP_RETURN
};
 
static char *ps_op_names[] =
{
"abs", "add", "and", "atan", "bitshift", "ceiling", "copy",
"cos", "cvi", "cvr", "div", "dup", "eq", "exch", "exp",
"false", "floor", "ge", "gt", "idiv", "index", "le", "ln",
"log", "lt", "mod", "mul", "ne", "neg", "not", "or", "pop",
"roll", "round", "sin", "sqrt", "sub", "true", "truncate",
"xor", "if", "ifelse", "return"
};
 
struct psobj_s
{
int type;
union
{
int b; /* boolean (stack only) */
int i; /* integer (stack and code) */
float f; /* real (stack and code) */
int op; /* operator (code only) */
int block; /* if/ifelse block pointer (code only) */
} u;
};
 
typedef struct ps_stack_s ps_stack;
 
struct ps_stack_s
{
psobj stack[100];
int sp;
};
 
void
pdf_debug_ps_stack(ps_stack *st)
{
int i;
 
printf("stack: ");
 
for (i = 0; i < st->sp; i++)
{
switch (st->stack[i].type)
{
case PS_BOOL:
if (st->stack[i].u.b)
printf("true ");
else
printf("false ");
break;
 
case PS_INT:
printf("%d ", st->stack[i].u.i);
break;
 
case PS_REAL:
printf("%g ", st->stack[i].u.f);
break;
}
}
printf("\n");
 
}
 
static void
ps_init_stack(ps_stack *st)
{
memset(st->stack, 0, sizeof(st->stack));
st->sp = 0;
}
 
static inline int ps_overflow(ps_stack *st, int n)
{
return n < 0 || st->sp + n >= nelem(st->stack);
}
 
static inline int ps_underflow(ps_stack *st, int n)
{
return n < 0 || st->sp - n < 0;
}
 
static inline int ps_is_type(ps_stack *st, int t)
{
return !ps_underflow(st, 1) && st->stack[st->sp - 1].type == t;
}
 
static inline int ps_is_type2(ps_stack *st, int t)
{
return !ps_underflow(st, 2) && st->stack[st->sp - 1].type == t && st->stack[st->sp - 2].type == t;
}
 
static void
ps_push_bool(ps_stack *st, int b)
{
if (!ps_overflow(st, 1))
{
st->stack[st->sp].type = PS_BOOL;
st->stack[st->sp].u.b = b;
st->sp++;
}
}
 
static void
ps_push_int(ps_stack *st, int n)
{
if (!ps_overflow(st, 1))
{
st->stack[st->sp].type = PS_INT;
st->stack[st->sp].u.i = n;
st->sp++;
}
}
 
static void
ps_push_real(ps_stack *st, float n)
{
if (!ps_overflow(st, 1))
{
st->stack[st->sp].type = PS_REAL;
st->stack[st->sp].u.f = n;
st->sp++;
}
}
 
static int
ps_pop_bool(ps_stack *st)
{
if (!ps_underflow(st, 1))
{
if (ps_is_type(st, PS_BOOL))
return st->stack[--st->sp].u.b;
}
return 0;
}
 
static int
ps_pop_int(ps_stack *st)
{
if (!ps_underflow(st, 1))
{
if (ps_is_type(st, PS_INT))
return st->stack[--st->sp].u.i;
if (ps_is_type(st, PS_REAL))
return st->stack[--st->sp].u.f;
}
return 0;
}
 
static float
ps_pop_real(ps_stack *st)
{
if (!ps_underflow(st, 1))
{
if (ps_is_type(st, PS_INT))
return st->stack[--st->sp].u.i;
if (ps_is_type(st, PS_REAL))
return st->stack[--st->sp].u.f;
}
return 0;
}
 
static void
ps_copy(ps_stack *st, int n)
{
if (!ps_underflow(st, n) && !ps_overflow(st, n))
{
memcpy(st->stack + st->sp, st->stack + st->sp - n, n * sizeof(psobj));
st->sp += n;
}
}
 
static void
ps_roll(ps_stack *st, int n, int j)
{
psobj tmp;
int i;
 
if (ps_underflow(st, n) || j == 0 || n == 0)
return;
 
if (j >= 0)
{
j %= n;
}
else
{
j = -j % n;
if (j != 0)
j = n - j;
}
 
for (i = 0; i < j; i++)
{
tmp = st->stack[st->sp - 1];
memmove(st->stack + st->sp - n + 1, st->stack + st->sp - n, n * sizeof(psobj));
st->stack[st->sp - n] = tmp;
}
}
 
static void
ps_index(ps_stack *st, int n)
{
if (!ps_overflow(st, 1) && !ps_underflow(st, n))
{
st->stack[st->sp] = st->stack[st->sp - n - 1];
st->sp++;
}
}
 
static void
ps_run(psobj *code, ps_stack *st, int pc)
{
int i1, i2;
float r1, r2;
int b1, b2;
 
while (1)
{
switch (code[pc].type)
{
case PS_INT:
ps_push_int(st, code[pc++].u.i);
break;
 
case PS_REAL:
ps_push_real(st, code[pc++].u.f);
break;
 
case PS_OPERATOR:
switch (code[pc++].u.op)
{
case PS_OP_ABS:
if (ps_is_type(st, PS_INT))
ps_push_int(st, abs(ps_pop_int(st)));
else
ps_push_real(st, fabsf(ps_pop_real(st)));
break;
 
case PS_OP_ADD:
if (ps_is_type2(st, PS_INT)) {
i2 = ps_pop_int(st);
i1 = ps_pop_int(st);
ps_push_int(st, i1 + i2);
}
else {
r2 = ps_pop_real(st);
r1 = ps_pop_real(st);
ps_push_real(st, r1 + r2);
}
break;
 
case PS_OP_AND:
if (ps_is_type2(st, PS_INT)) {
i2 = ps_pop_int(st);
i1 = ps_pop_int(st);
ps_push_int(st, i1 & i2);
}
else {
b2 = ps_pop_bool(st);
b1 = ps_pop_bool(st);
ps_push_bool(st, b1 && b2);
}
break;
 
case PS_OP_ATAN:
r2 = ps_pop_real(st);
r1 = ps_pop_real(st);
r1 = atan2f(r1, r2) * RADIAN;
if (r1 < 0)
r1 += 360;
ps_push_real(st, r1);
break;
 
case PS_OP_BITSHIFT:
i2 = ps_pop_int(st);
i1 = ps_pop_int(st);
if (i2 > 0)
ps_push_int(st, i1 << i2);
else if (i2 < 0)
ps_push_int(st, (int)((unsigned int)i1 >> i2));
else
ps_push_int(st, i1);
break;
 
case PS_OP_CEILING:
r1 = ps_pop_real(st);
ps_push_real(st, ceilf(r1));
break;
 
case PS_OP_COPY:
ps_copy(st, ps_pop_int(st));
break;
 
case PS_OP_COS:
r1 = ps_pop_real(st);
ps_push_real(st, cosf(r1/RADIAN));
break;
 
case PS_OP_CVI:
ps_push_int(st, ps_pop_int(st));
break;
 
case PS_OP_CVR:
ps_push_real(st, ps_pop_real(st));
break;
 
case PS_OP_DIV:
r2 = ps_pop_real(st);
r1 = ps_pop_real(st);
ps_push_real(st, r1 / r2);
break;
 
case PS_OP_DUP:
ps_copy(st, 1);
break;
 
case PS_OP_EQ:
if (ps_is_type2(st, PS_BOOL)) {
b2 = ps_pop_bool(st);
b1 = ps_pop_bool(st);
ps_push_bool(st, b1 == b2);
}
else if (ps_is_type2(st, PS_INT)) {
i2 = ps_pop_int(st);
i1 = ps_pop_int(st);
ps_push_bool(st, i1 == i2);
}
else {
r2 = ps_pop_real(st);
r1 = ps_pop_real(st);
ps_push_bool(st, r1 == r2);
}
break;
 
case PS_OP_EXCH:
ps_roll(st, 2, 1);
break;
 
case PS_OP_EXP:
r2 = ps_pop_real(st);
r1 = ps_pop_real(st);
ps_push_real(st, powf(r1, r2));
break;
 
case PS_OP_FALSE:
ps_push_bool(st, 0);
break;
 
case PS_OP_FLOOR:
r1 = ps_pop_real(st);
ps_push_real(st, floorf(r1));
break;
 
case PS_OP_GE:
if (ps_is_type2(st, PS_INT)) {
i2 = ps_pop_int(st);
i1 = ps_pop_int(st);
ps_push_bool(st, i1 >= i2);
}
else {
r2 = ps_pop_real(st);
r1 = ps_pop_real(st);
ps_push_bool(st, r1 >= r2);
}
break;
 
case PS_OP_GT:
if (ps_is_type2(st, PS_INT)) {
i2 = ps_pop_int(st);
i1 = ps_pop_int(st);
ps_push_bool(st, i1 > i2);
}
else {
r2 = ps_pop_real(st);
r1 = ps_pop_real(st);
ps_push_bool(st, r1 > r2);
}
break;
 
case PS_OP_IDIV:
i2 = ps_pop_int(st);
i1 = ps_pop_int(st);
ps_push_int(st, i1 / i2);
break;
 
case PS_OP_INDEX:
ps_index(st, ps_pop_int(st));
break;
 
case PS_OP_LE:
if (ps_is_type2(st, PS_INT)) {
i2 = ps_pop_int(st);
i1 = ps_pop_int(st);
ps_push_bool(st, i1 <= i2);
}
else {
r2 = ps_pop_real(st);
r1 = ps_pop_real(st);
ps_push_bool(st, r1 <= r2);
}
break;
 
case PS_OP_LN:
r1 = ps_pop_real(st);
ps_push_real(st, logf(r1));
break;
 
case PS_OP_LOG:
r1 = ps_pop_real(st);
ps_push_real(st, log10f(r1));
break;
 
case PS_OP_LT:
if (ps_is_type2(st, PS_INT)) {
i2 = ps_pop_int(st);
i1 = ps_pop_int(st);
ps_push_bool(st, i1 < i2);
}
else {
r2 = ps_pop_real(st);
r1 = ps_pop_real(st);
ps_push_bool(st, r1 < r2);
}
break;
 
case PS_OP_MOD:
i2 = ps_pop_int(st);
i1 = ps_pop_int(st);
ps_push_int(st, i1 % i2);
break;
 
case PS_OP_MUL:
if (ps_is_type2(st, PS_INT)) {
i2 = ps_pop_int(st);
i1 = ps_pop_int(st);
ps_push_int(st, i1 * i2);
}
else {
r2 = ps_pop_real(st);
r1 = ps_pop_real(st);
ps_push_real(st, r1 * r2);
}
break;
 
case PS_OP_NE:
if (ps_is_type2(st, PS_BOOL)) {
b2 = ps_pop_bool(st);
b1 = ps_pop_bool(st);
ps_push_bool(st, b1 != b2);
}
else if (ps_is_type2(st, PS_INT)) {
i2 = ps_pop_int(st);
i1 = ps_pop_int(st);
ps_push_bool(st, i1 != i2);
}
else {
r2 = ps_pop_real(st);
r1 = ps_pop_real(st);
ps_push_bool(st, r1 != r2);
}
break;
 
case PS_OP_NEG:
if (ps_is_type(st, PS_INT))
ps_push_int(st, -ps_pop_int(st));
else
ps_push_real(st, -ps_pop_real(st));
break;
 
case PS_OP_NOT:
if (ps_is_type(st, PS_BOOL))
ps_push_bool(st, !ps_pop_bool(st));
else
ps_push_int(st, ~ps_pop_int(st));
break;
 
case PS_OP_OR:
if (ps_is_type2(st, PS_BOOL)) {
b2 = ps_pop_bool(st);
b1 = ps_pop_bool(st);
ps_push_bool(st, b1 || b2);
}
else {
i2 = ps_pop_int(st);
i1 = ps_pop_int(st);
ps_push_int(st, i1 | i2);
}
break;
 
case PS_OP_POP:
if (!ps_underflow(st, 1))
st->sp--;
break;
 
case PS_OP_ROLL:
i2 = ps_pop_int(st);
i1 = ps_pop_int(st);
ps_roll(st, i1, i2);
break;
 
case PS_OP_ROUND:
if (!ps_is_type(st, PS_INT)) {
r1 = ps_pop_real(st);
ps_push_real(st, (r1 >= 0) ? floorf(r1 + 0.5f) : ceilf(r1 - 0.5f));
}
break;
 
case PS_OP_SIN:
r1 = ps_pop_real(st);
ps_push_real(st, sinf(r1/RADIAN));
break;
 
case PS_OP_SQRT:
r1 = ps_pop_real(st);
ps_push_real(st, sqrtf(r1));
break;
 
case PS_OP_SUB:
if (ps_is_type2(st, PS_INT)) {
i2 = ps_pop_int(st);
i1 = ps_pop_int(st);
ps_push_int(st, i1 - i2);
}
else {
r2 = ps_pop_real(st);
r1 = ps_pop_real(st);
ps_push_real(st, r1 - r2);
}
break;
 
case PS_OP_TRUE:
ps_push_bool(st, 1);
break;
 
case PS_OP_TRUNCATE:
if (!ps_is_type(st, PS_INT)) {
r1 = ps_pop_real(st);
ps_push_real(st, (r1 >= 0) ? floorf(r1) : ceilf(r1));
}
break;
 
case PS_OP_XOR:
if (ps_is_type2(st, PS_BOOL)) {
b2 = ps_pop_bool(st);
b1 = ps_pop_bool(st);
ps_push_bool(st, b1 ^ b2);
}
else {
i2 = ps_pop_int(st);
i1 = ps_pop_int(st);
ps_push_int(st, i1 ^ i2);
}
break;
 
case PS_OP_IF:
b1 = ps_pop_bool(st);
if (b1)
ps_run(code, st, code[pc + 1].u.block);
pc = code[pc + 2].u.block;
break;
 
case PS_OP_IFELSE:
b1 = ps_pop_bool(st);
if (b1)
ps_run(code, st, code[pc + 1].u.block);
else
ps_run(code, st, code[pc + 0].u.block);
pc = code[pc + 2].u.block;
break;
 
case PS_OP_RETURN:
return;
 
default:
fz_warn("foreign operator in calculator function");
return;
}
break;
 
default:
fz_warn("foreign object in calculator function");
return;
}
}
}
 
static void
resize_code(pdf_function *func, int newsize)
{
if (newsize >= func->u.p.cap)
{
func->u.p.cap = func->u.p.cap + 64;
func->u.p.code = fz_realloc(func->u.p.code, func->u.p.cap, sizeof(psobj));
}
}
 
static fz_error
parse_code(pdf_function *func, fz_stream *stream, int *codeptr)
{
fz_error error;
char buf[64];
int len;
int tok;
int opptr, elseptr, ifptr;
int a, b, mid, cmp;
 
memset(buf, 0, sizeof(buf));
 
while (1)
{
error = pdf_lex(&tok, stream, buf, sizeof buf, &len);
if (error)
return fz_rethrow(error, "calculator function lexical error");
 
switch(tok)
{
case PDF_TOK_EOF:
return fz_throw("truncated calculator function");
 
case PDF_TOK_INT:
resize_code(func, *codeptr);
func->u.p.code[*codeptr].type = PS_INT;
func->u.p.code[*codeptr].u.i = atoi(buf);
++*codeptr;
break;
 
case PDF_TOK_REAL:
resize_code(func, *codeptr);
func->u.p.code[*codeptr].type = PS_REAL;
func->u.p.code[*codeptr].u.f = fz_atof(buf);
++*codeptr;
break;
 
case PDF_TOK_OPEN_BRACE:
opptr = *codeptr;
*codeptr += 4;
 
resize_code(func, *codeptr);
 
ifptr = *codeptr;
error = parse_code(func, stream, codeptr);
if (error)
return fz_rethrow(error, "error in 'if' branch");
 
error = pdf_lex(&tok, stream, buf, sizeof buf, &len);
if (error)
return fz_rethrow(error, "calculator function syntax error");
 
if (tok == PDF_TOK_OPEN_BRACE)
{
elseptr = *codeptr;
error = parse_code(func, stream, codeptr);
if (error)
return fz_rethrow(error, "error in 'else' branch");
 
error = pdf_lex(&tok, stream, buf, sizeof buf, &len);
if (error)
return fz_rethrow(error, "calculator function syntax error");
}
else
{
elseptr = -1;
}
 
if (tok == PDF_TOK_KEYWORD)
{
if (!strcmp(buf, "if"))
{
if (elseptr >= 0)
return fz_throw("too many branches for 'if'");
func->u.p.code[opptr].type = PS_OPERATOR;
func->u.p.code[opptr].u.op = PS_OP_IF;
func->u.p.code[opptr+2].type = PS_BLOCK;
func->u.p.code[opptr+2].u.block = ifptr;
func->u.p.code[opptr+3].type = PS_BLOCK;
func->u.p.code[opptr+3].u.block = *codeptr;
}
else if (!strcmp(buf, "ifelse"))
{
if (elseptr < 0)
return fz_throw("not enough branches for 'ifelse'");
func->u.p.code[opptr].type = PS_OPERATOR;
func->u.p.code[opptr].u.op = PS_OP_IFELSE;
func->u.p.code[opptr+1].type = PS_BLOCK;
func->u.p.code[opptr+1].u.block = elseptr;
func->u.p.code[opptr+2].type = PS_BLOCK;
func->u.p.code[opptr+2].u.block = ifptr;
func->u.p.code[opptr+3].type = PS_BLOCK;
func->u.p.code[opptr+3].u.block = *codeptr;
}
else
{
return fz_throw("unknown keyword in 'if-else' context: '%s'", buf);
}
}
else
{
return fz_throw("missing keyword in 'if-else' context");
}
break;
 
case PDF_TOK_CLOSE_BRACE:
resize_code(func, *codeptr);
func->u.p.code[*codeptr].type = PS_OPERATOR;
func->u.p.code[*codeptr].u.op = PS_OP_RETURN;
++*codeptr;
return fz_okay;
 
case PDF_TOK_KEYWORD:
cmp = -1;
a = -1;
b = nelem(ps_op_names);
while (b - a > 1)
{
mid = (a + b) / 2;
cmp = strcmp(buf, ps_op_names[mid]);
if (cmp > 0)
a = mid;
else if (cmp < 0)
b = mid;
else
a = b = mid;
}
if (cmp != 0)
return fz_throw("unknown operator: '%s'", buf);
 
resize_code(func, *codeptr);
func->u.p.code[*codeptr].type = PS_OPERATOR;
func->u.p.code[*codeptr].u.op = a;
++*codeptr;
break;
 
default:
return fz_throw("calculator function syntax error");
}
}
}
 
static fz_error
load_postscript_func(pdf_function *func, pdf_xref *xref, fz_obj *dict, int num, int gen)
{
fz_error error;
fz_stream *stream;
int codeptr;
char buf[64];
int tok;
int len;
 
error = pdf_open_stream(&stream, xref, num, gen);
if (error)
return fz_rethrow(error, "cannot open calculator function stream");
 
error = pdf_lex(&tok, stream, buf, sizeof buf, &len);
if (error)
{
fz_close(stream);
return fz_rethrow(error, "stream is not a calculator function");
}
 
if (tok != PDF_TOK_OPEN_BRACE)
{
fz_close(stream);
return fz_throw("stream is not a calculator function");
}
 
func->u.p.code = NULL;
func->u.p.cap = 0;
 
codeptr = 0;
error = parse_code(func, stream, &codeptr);
if (error)
{
fz_close(stream);
return fz_rethrow(error, "cannot parse calculator function (%d %d R)", num, gen);
}
 
fz_close(stream);
return fz_okay;
}
 
static void
eval_postscript_func(pdf_function *func, float *in, float *out)
{
ps_stack st;
float x;
int i;
 
ps_init_stack(&st);
 
for (i = 0; i < func->m; i++)
{
x = CLAMP(in[i], func->domain[i][0], func->domain[i][1]);
ps_push_real(&st, x);
}
 
ps_run(func->u.p.code, &st, 0);
 
for (i = func->n - 1; i >= 0; i--)
{
x = ps_pop_real(&st);
out[i] = CLAMP(x, func->range[i][0], func->range[i][1]);
}
}
 
/*
* Sample function
*/
 
static fz_error
load_sample_func(pdf_function *func, pdf_xref *xref, fz_obj *dict, int num, int gen)
{
fz_error error;
fz_stream *stream;
fz_obj *obj;
int samplecount;
int bps;
int i;
 
func->u.sa.samples = NULL;
 
obj = fz_dict_gets(dict, "Size");
if (!fz_is_array(obj) || fz_array_len(obj) != func->m)
return fz_throw("malformed /Size");
for (i = 0; i < func->m; i++)
func->u.sa.size[i] = fz_to_int(fz_array_get(obj, i));
 
obj = fz_dict_gets(dict, "BitsPerSample");
if (!fz_is_int(obj))
return fz_throw("malformed /BitsPerSample");
func->u.sa.bps = bps = fz_to_int(obj);
 
obj = fz_dict_gets(dict, "Encode");
if (fz_is_array(obj))
{
if (fz_array_len(obj) != func->m * 2)
return fz_throw("malformed /Encode");
for (i = 0; i < func->m; i++)
{
func->u.sa.encode[i][0] = fz_to_real(fz_array_get(obj, i*2+0));
func->u.sa.encode[i][1] = fz_to_real(fz_array_get(obj, i*2+1));
}
}
else
{
for (i = 0; i < func->m; i++)
{
func->u.sa.encode[i][0] = 0;
func->u.sa.encode[i][1] = func->u.sa.size[i] - 1;
}
}
 
obj = fz_dict_gets(dict, "Decode");
if (fz_is_array(obj))
{
if (fz_array_len(obj) != func->n * 2)
return fz_throw("malformed /Decode");
for (i = 0; i < func->n; i++)
{
func->u.sa.decode[i][0] = fz_to_real(fz_array_get(obj, i*2+0));
func->u.sa.decode[i][1] = fz_to_real(fz_array_get(obj, i*2+1));
}
}
else
{
for (i = 0; i < func->n; i++)
{
func->u.sa.decode[i][0] = func->range[i][0];
func->u.sa.decode[i][1] = func->range[i][1];
}
}
 
for (i = 0, samplecount = func->n; i < func->m; i++)
samplecount *= func->u.sa.size[i];
 
func->u.sa.samples = fz_calloc(samplecount, sizeof(float));
 
error = pdf_open_stream(&stream, xref, num, gen);
if (error)
return fz_rethrow(error, "cannot open samples stream (%d %d R)", num, gen);
 
/* read samples */
for (i = 0; i < samplecount; i++)
{
unsigned int x;
float s;
 
if (fz_is_eof_bits(stream))
{
fz_close(stream);
return fz_throw("truncated sample stream");
}
 
switch (bps)
{
case 1: s = fz_read_bits(stream, 1); break;
case 2: s = fz_read_bits(stream, 2) / 3.0f; break;
case 4: s = fz_read_bits(stream, 4) / 15.0f; break;
case 8: s = fz_read_byte(stream) / 255.0f; break;
case 12: s = fz_read_bits(stream, 12) / 4095.0f; break;
case 16:
x = fz_read_byte(stream) << 8;
x |= fz_read_byte(stream);
s = x / 65535.0f;
break;
case 24:
x = fz_read_byte(stream) << 16;
x |= fz_read_byte(stream) << 8;
x |= fz_read_byte(stream);
s = x / 16777215.0f;
break;
case 32:
x = fz_read_byte(stream) << 24;
x |= fz_read_byte(stream) << 16;
x |= fz_read_byte(stream) << 8;
x |= fz_read_byte(stream);
s = x / 4294967295.0f;
break;
default:
fz_close(stream);
return fz_throw("sample stream bit depth %d unsupported", bps);
}
 
func->u.sa.samples[i] = s;
}
 
fz_close(stream);
 
return fz_okay;
}
 
static float
interpolate_sample(pdf_function *func, int *scale, int *e0, int *e1, float *efrac, int dim, int idx)
{
float a, b;
int idx0, idx1;
 
idx0 = e0[dim] * scale[dim] + idx;
idx1 = e1[dim] * scale[dim] + idx;
 
if (dim == 0)
{
a = func->u.sa.samples[idx0];
b = func->u.sa.samples[idx1];
}
else
{
a = interpolate_sample(func, scale, e0, e1, efrac, dim - 1, idx0);
b = interpolate_sample(func, scale, e0, e1, efrac, dim - 1, idx1);
}
 
return a + (b - a) * efrac[dim];
}
 
static void
eval_sample_func(pdf_function *func, float *in, float *out)
{
int e0[MAXM], e1[MAXM], scale[MAXM];
float efrac[MAXM];
float x;
int i;
 
/* encode input coordinates */
for (i = 0; i < func->m; i++)
{
x = CLAMP(in[i], func->domain[i][0], func->domain[i][1]);
x = lerp(x, func->domain[i][0], func->domain[i][1],
func->u.sa.encode[i][0], func->u.sa.encode[i][1]);
x = CLAMP(x, 0, func->u.sa.size[i] - 1);
e0[i] = floorf(x);
e1[i] = ceilf(x);
efrac[i] = x - floorf(x);
}
 
scale[0] = func->n;
for (i = 1; i < func->m; i++)
scale[i] = scale[i - 1] * func->u.sa.size[i];
 
for (i = 0; i < func->n; i++)
{
if (func->m == 1)
{
float a = func->u.sa.samples[e0[0] * func->n + i];
float b = func->u.sa.samples[e1[0] * func->n + i];
 
float ab = a + (b - a) * efrac[0];
 
out[i] = lerp(ab, 0, 1, func->u.sa.decode[i][0], func->u.sa.decode[i][1]);
out[i] = CLAMP(out[i], func->range[i][0], func->range[i][1]);
}
 
else if (func->m == 2)
{
int s0 = func->n;
int s1 = s0 * func->u.sa.size[0];
 
float a = func->u.sa.samples[e0[0] * s0 + e0[1] * s1 + i];
float b = func->u.sa.samples[e1[0] * s0 + e0[1] * s1 + i];
float c = func->u.sa.samples[e0[0] * s0 + e1[1] * s1 + i];
float d = func->u.sa.samples[e1[0] * s0 + e1[1] * s1 + i];
 
float ab = a + (b - a) * efrac[0];
float cd = c + (d - c) * efrac[0];
float abcd = ab + (cd - ab) * efrac[1];
 
out[i] = lerp(abcd, 0, 1, func->u.sa.decode[i][0], func->u.sa.decode[i][1]);
out[i] = CLAMP(out[i], func->range[i][0], func->range[i][1]);
}
 
else
{
float x = interpolate_sample(func, scale, e0, e1, efrac, func->m - 1, i);
out[i] = lerp(x, 0, 1, func->u.sa.decode[i][0], func->u.sa.decode[i][1]);
out[i] = CLAMP(out[i], func->range[i][0], func->range[i][1]);
}
}
}
 
/*
* Exponential function
*/
 
static fz_error
load_exponential_func(pdf_function *func, fz_obj *dict)
{
fz_obj *obj;
int i;
 
if (func->m != 1)
return fz_throw("/Domain must be one dimension (%d)", func->m);
 
obj = fz_dict_gets(dict, "N");
if (!fz_is_int(obj) && !fz_is_real(obj))
return fz_throw("malformed /N");
func->u.e.n = fz_to_real(obj);
 
obj = fz_dict_gets(dict, "C0");
if (fz_is_array(obj))
{
func->n = fz_array_len(obj);
if (func->n >= MAXN)
return fz_throw("exponential function result array out of range");
for (i = 0; i < func->n; i++)
func->u.e.c0[i] = fz_to_real(fz_array_get(obj, i));
}
else
{
func->n = 1;
func->u.e.c0[0] = 0;
}
 
obj = fz_dict_gets(dict, "C1");
if (fz_is_array(obj))
{
if (fz_array_len(obj) != func->n)
return fz_throw("/C1 must match /C0 length");
for (i = 0; i < func->n; i++)
func->u.e.c1[i] = fz_to_real(fz_array_get(obj, i));
}
else
{
if (func->n != 1)
return fz_throw("/C1 must match /C0 length");
func->u.e.c1[0] = 1;
}
 
return fz_okay;
}
 
static void
eval_exponential_func(pdf_function *func, float in, float *out)
{
float x = in;
float tmp;
int i;
 
x = CLAMP(x, func->domain[0][0], func->domain[0][1]);
 
/* constraint */
if ((func->u.e.n != (int)func->u.e.n && x < 0) || (func->u.e.n < 0 && x == 0))
{
fz_warn("constraint error");
return;
}
 
tmp = powf(x, func->u.e.n);
for (i = 0; i < func->n; i++)
{
out[i] = func->u.e.c0[i] + tmp * (func->u.e.c1[i] - func->u.e.c0[i]);
if (func->has_range)
out[i] = CLAMP(out[i], func->range[i][0], func->range[i][1]);
}
}
 
/*
* Stitching function
*/
 
static fz_error
load_stitching_func(pdf_function *func, pdf_xref *xref, fz_obj *dict)
{
pdf_function **funcs;
fz_error error;
fz_obj *obj;
fz_obj *sub;
fz_obj *num;
int k;
int i;
 
func->u.st.k = 0;
 
if (func->m != 1)
return fz_throw("/Domain must be one dimension (%d)", func->m);
 
obj = fz_dict_gets(dict, "Functions");
if (!fz_is_array(obj))
return fz_throw("stitching function has no input functions");
{
k = fz_array_len(obj);
 
func->u.st.funcs = fz_calloc(k, sizeof(pdf_function*));
func->u.st.bounds = fz_calloc(k - 1, sizeof(float));
func->u.st.encode = fz_calloc(k * 2, sizeof(float));
funcs = func->u.st.funcs;
 
for (i = 0; i < k; i++)
{
sub = fz_array_get(obj, i);
error = pdf_load_function(&funcs[i], xref, sub);
if (error)
return fz_rethrow(error, "cannot load sub function %d (%d %d R)", i, fz_to_num(sub), fz_to_gen(sub));
if (funcs[i]->m != 1 || funcs[i]->n != funcs[0]->n)
return fz_throw("sub function %d /Domain or /Range mismatch", i);
func->u.st.k ++;
}
 
if (!func->n)
func->n = funcs[0]->n;
else if (func->n != funcs[0]->n)
return fz_throw("sub function /Domain or /Range mismatch");
}
 
obj = fz_dict_gets(dict, "Bounds");
if (!fz_is_array(obj))
return fz_throw("stitching function has no bounds");
{
if (!fz_is_array(obj) || fz_array_len(obj) != k - 1)
return fz_throw("malformed /Bounds (not array or wrong length)");
 
for (i = 0; i < k-1; i++)
{
num = fz_array_get(obj, i);
if (!fz_is_int(num) && !fz_is_real(num))
return fz_throw("malformed /Bounds (item not real)");
func->u.st.bounds[i] = fz_to_real(num);
if (i && func->u.st.bounds[i-1] > func->u.st.bounds[i])
return fz_throw("malformed /Bounds (item not monotonic)");
}
 
if (k != 1 && (func->domain[0][0] > func->u.st.bounds[0] ||
func->domain[0][1] < func->u.st.bounds[k-2]))
fz_warn("malformed shading function bounds (domain mismatch), proceeding anyway.");
}
 
obj = fz_dict_gets(dict, "Encode");
if (!fz_is_array(obj))
return fz_throw("stitching function is missing encoding");
{
if (!fz_is_array(obj) || fz_array_len(obj) != k * 2)
return fz_throw("malformed /Encode");
for (i = 0; i < k; i++)
{
func->u.st.encode[i*2+0] = fz_to_real(fz_array_get(obj, i*2+0));
func->u.st.encode[i*2+1] = fz_to_real(fz_array_get(obj, i*2+1));
}
}
 
return fz_okay;
}
 
static void
eval_stitching_func(pdf_function *func, float in, float *out)
{
float low, high;
int k = func->u.st.k;
float *bounds = func->u.st.bounds;
int i;
 
in = CLAMP(in, func->domain[0][0], func->domain[0][1]);
 
for (i = 0; i < k - 1; i++)
{
if (in < bounds[i])
break;
}
 
if (i == 0 && k == 1)
{
low = func->domain[0][0];
high = func->domain[0][1];
}
else if (i == 0)
{
low = func->domain[0][0];
high = bounds[0];
}
else if (i == k - 1)
{
low = bounds[k-2];
high = func->domain[0][1];
}
else
{
low = bounds[i-1];
high = bounds[i];
}
 
in = lerp(in, low, high, func->u.st.encode[i*2+0], func->u.st.encode[i*2+1]);
 
pdf_eval_function(func->u.st.funcs[i], &in, 1, out, func->n);
}
 
/*
* Common
*/
 
pdf_function *
pdf_keep_function(pdf_function *func)
{
func->refs ++;
return func;
}
 
void
pdf_drop_function(pdf_function *func)
{
int i;
if (--func->refs == 0)
{
switch(func->type)
{
case SAMPLE:
fz_free(func->u.sa.samples);
break;
case EXPONENTIAL:
break;
case STITCHING:
for (i = 0; i < func->u.st.k; i++)
pdf_drop_function(func->u.st.funcs[i]);
fz_free(func->u.st.funcs);
fz_free(func->u.st.bounds);
fz_free(func->u.st.encode);
break;
case POSTSCRIPT:
fz_free(func->u.p.code);
break;
}
fz_free(func);
}
}
 
fz_error
pdf_load_function(pdf_function **funcp, pdf_xref *xref, fz_obj *dict)
{
fz_error error;
pdf_function *func;
fz_obj *obj;
int i;
 
if ((*funcp = pdf_find_item(xref->store, pdf_drop_function, dict)))
{
pdf_keep_function(*funcp);
return fz_okay;
}
 
func = fz_malloc(sizeof(pdf_function));
memset(func, 0, sizeof(pdf_function));
func->refs = 1;
 
obj = fz_dict_gets(dict, "FunctionType");
func->type = fz_to_int(obj);
 
/* required for all */
obj = fz_dict_gets(dict, "Domain");
func->m = fz_array_len(obj) / 2;
for (i = 0; i < func->m; i++)
{
func->domain[i][0] = fz_to_real(fz_array_get(obj, i * 2 + 0));
func->domain[i][1] = fz_to_real(fz_array_get(obj, i * 2 + 1));
}
 
/* required for type0 and type4, optional otherwise */
obj = fz_dict_gets(dict, "Range");
if (fz_is_array(obj))
{
func->has_range = 1;
func->n = fz_array_len(obj) / 2;
for (i = 0; i < func->n; i++)
{
func->range[i][0] = fz_to_real(fz_array_get(obj, i * 2 + 0));
func->range[i][1] = fz_to_real(fz_array_get(obj, i * 2 + 1));
}
}
else
{
func->has_range = 0;
func->n = 0;
}
 
if (func->m >= MAXM || func->n >= MAXN)
{
fz_free(func);
return fz_throw("assert: /Domain or /Range too big");
}
 
switch(func->type)
{
case SAMPLE:
error = load_sample_func(func, xref, dict, fz_to_num(dict), fz_to_gen(dict));
if (error)
{
pdf_drop_function(func);
return fz_rethrow(error, "cannot load sampled function (%d %d R)", fz_to_num(dict), fz_to_gen(dict));
}
break;
 
case EXPONENTIAL:
error = load_exponential_func(func, dict);
if (error)
{
pdf_drop_function(func);
return fz_rethrow(error, "cannot load exponential function (%d %d R)", fz_to_num(dict), fz_to_gen(dict));
}
break;
 
case STITCHING:
error = load_stitching_func(func, xref, dict);
if (error)
{
pdf_drop_function(func);
return fz_rethrow(error, "cannot load stitching function (%d %d R)", fz_to_num(dict), fz_to_gen(dict));
}
break;
 
case POSTSCRIPT:
error = load_postscript_func(func, xref, dict, fz_to_num(dict), fz_to_gen(dict));
if (error)
{
pdf_drop_function(func);
return fz_rethrow(error, "cannot load calculator function (%d %d R)", fz_to_num(dict), fz_to_gen(dict));
}
break;
 
default:
fz_free(func);
return fz_throw("unknown function type (%d %d R)", fz_to_num(dict), fz_to_gen(dict));
}
 
pdf_store_item(xref->store, pdf_keep_function, pdf_drop_function, dict, func);
 
*funcp = func;
return fz_okay;
}
 
void
pdf_eval_function(pdf_function *func, float *in, int inlen, float *out, int outlen)
{
memset(out, 0, sizeof(float) * outlen);
 
if (inlen != func->m)
{
fz_warn("tried to evaluate function with wrong number of inputs");
return;
}
if (func->n != outlen)
{
fz_warn("tried to evaluate function with wrong number of outputs");
return;
}
 
switch(func->type)
{
case SAMPLE: eval_sample_func(func, in, out); break;
case EXPONENTIAL: eval_exponential_func(func, *in, out); break;
case STITCHING: eval_stitching_func(func, *in, out); break;
case POSTSCRIPT: eval_postscript_func(func, in, out); break;
}
}
 
/*
* Debugging prints
*/
 
static void
pdf_debug_indent(char *prefix, int level, char *suffix)
{
int i;
 
printf("%s", prefix);
 
for (i = 0; i < level; i++)
printf("\t");
 
printf("%s", suffix);
}
 
static void
pdf_debug_ps_func_code(psobj *funccode, psobj *code, int level)
{
int eof, wasop;
 
pdf_debug_indent("", level, "{");
 
/* Print empty blocks as { }, instead of separating braces on different lines. */
if (code->type == PS_OPERATOR && code->u.op == PS_OP_RETURN)
{
printf(" } ");
return;
}
 
pdf_debug_indent("\n", ++level, "");
 
eof = 0;
wasop = 0;
while (!eof)
{
switch (code->type)
{
case PS_INT:
if (wasop)
pdf_debug_indent("\n", level, "");
 
printf("%d ", code->u.i);
wasop = 0;
code++;
break;
 
case PS_REAL:
if (wasop)
pdf_debug_indent("\n", level, "");
 
printf("%g ", code->u.f);
wasop = 0;
code++;
break;
 
case PS_OPERATOR:
if (code->u.op == PS_OP_RETURN)
{
printf("\n");
eof = 1;
}
else if (code->u.op == PS_OP_IF)
{
printf("\n");
pdf_debug_ps_func_code(funccode, &funccode[(code + 2)->u.block], level);
 
printf("%s", ps_op_names[code->u.op]);
code = &funccode[(code + 3)->u.block];
if (code->type != PS_OPERATOR || code->u.op != PS_OP_RETURN)
pdf_debug_indent("\n", level, "");
 
wasop = 0;
}
else if (code->u.op == PS_OP_IFELSE)
{
printf("\n");
pdf_debug_ps_func_code(funccode, &funccode[(code + 2)->u.block], level);
 
printf("\n");
pdf_debug_ps_func_code(funccode, &funccode[(code + 1)->u.block], level);
 
printf("%s", ps_op_names[code->u.op]);
code = &funccode[(code + 3)->u.block];
if (code->type != PS_OPERATOR || code->u.op != PS_OP_RETURN)
pdf_debug_indent("\n", level, "");
 
wasop = 0;
}
else
{
printf("%s ", ps_op_names[code->u.op]);
code++;
wasop = 1;
}
break;
}
}
 
pdf_debug_indent("", --level, "} ");
}
 
static void
pdf_debug_function_imp(pdf_function *func, int level)
{
int i;
 
pdf_debug_indent("", level, "function {\n");
 
pdf_debug_indent("", ++level, "");
switch (func->type)
{
case SAMPLE:
printf("sampled");
break;
case EXPONENTIAL:
printf("exponential");
break;
case STITCHING:
printf("stitching");
break;
case POSTSCRIPT:
printf("postscript");
break;
}
 
pdf_debug_indent("\n", level, "");
printf("%d input -> %d output\n", func->m, func->n);
 
pdf_debug_indent("", level, "domain ");
for (i = 0; i < func->m; i++)
printf("%g %g ", func->domain[i][0], func->domain[i][1]);
printf("\n");
 
if (func->has_range)
{
pdf_debug_indent("", level, "range ");
for (i = 0; i < func->n; i++)
printf("%g %g ", func->range[i][0], func->range[i][1]);
printf("\n");
}
 
switch (func->type)
{
case SAMPLE:
pdf_debug_indent("", level, "");
printf("bps: %d\n", func->u.sa.bps);
 
pdf_debug_indent("", level, "");
printf("size: [ ");
for (i = 0; i < func->m; i++)
printf("%d ", func->u.sa.size[i]);
printf("]\n");
 
pdf_debug_indent("", level, "");
printf("encode: [ ");
for (i = 0; i < func->m; i++)
printf("%g %g ", func->u.sa.encode[i][0], func->u.sa.encode[i][1]);
printf("]\n");
 
pdf_debug_indent("", level, "");
printf("decode: [ ");
for (i = 0; i < func->m; i++)
printf("%g %g ", func->u.sa.decode[i][0], func->u.sa.decode[i][1]);
printf("]\n");
break;
 
case EXPONENTIAL:
pdf_debug_indent("", level, "");
printf("n: %g\n", func->u.e.n);
 
pdf_debug_indent("", level, "");
printf("c0: [ ");
for (i = 0; i < func->n; i++)
printf("%g ", func->u.e.c0[i]);
printf("]\n");
 
pdf_debug_indent("", level, "");
printf("c1: [ ");
for (i = 0; i < func->n; i++)
printf("%g ", func->u.e.c1[i]);
printf("]\n");
break;
 
case STITCHING:
pdf_debug_indent("", level, "");
printf("%d functions\n", func->u.st.k);
 
pdf_debug_indent("", level, "");
printf("bounds: [ ");
for (i = 0; i < func->u.st.k - 1; i++)
printf("%g ", func->u.st.bounds[i]);
printf("]\n");
 
pdf_debug_indent("", level, "");
printf("encode: [ ");
for (i = 0; i < func->u.st.k * 2; i++)
printf("%g ", func->u.st.encode[i]);
printf("]\n");
 
for (i = 0; i < func->u.st.k; i++)
pdf_debug_function_imp(func->u.st.funcs[i], level);
break;
 
case POSTSCRIPT:
pdf_debug_ps_func_code(func->u.p.code, func->u.p.code, level);
printf("\n");
break;
}
 
pdf_debug_indent("", --level, "}\n");
}
 
void
pdf_debug_function(pdf_function *func)
{
pdf_debug_function_imp(func, 0);
}
/contrib/media/updf/pdf/pdf_image.c
0,0 → 1,354
#include "fitz.h"
#include "mupdf.h"
 
/* TODO: store JPEG compressed samples */
/* TODO: store flate compressed samples */
 
static fz_error pdf_load_jpx_image(fz_pixmap **imgp, pdf_xref *xref, fz_obj *dict);
 
static void
pdf_mask_color_key(fz_pixmap *pix, int n, int *colorkey)
{
unsigned char *p = pix->samples;
int len = pix->w * pix->h;
int k, t;
while (len--)
{
t = 1;
for (k = 0; k < n; k++)
if (p[k] < colorkey[k * 2] || p[k] > colorkey[k * 2 + 1])
t = 0;
if (t)
for (k = 0; k < pix->n; k++)
p[k] = 0;
p += pix->n;
}
}
 
static fz_error
pdf_load_image_imp(fz_pixmap **imgp, pdf_xref *xref, fz_obj *rdb, fz_obj *dict, fz_stream *cstm, int forcemask)
{
fz_stream *stm;
fz_pixmap *tile;
fz_obj *obj, *res;
fz_error error;
 
int w, h, bpc, n;
int imagemask;
int interpolate;
int indexed;
fz_colorspace *colorspace;
fz_pixmap *mask; /* explicit mask/softmask image */
int usecolorkey;
int colorkey[FZ_MAX_COLORS * 2];
float decode[FZ_MAX_COLORS * 2];
 
int stride;
unsigned char *samples;
int i, len;
 
/* special case for JPEG2000 images */
if (pdf_is_jpx_image(dict))
{
tile = NULL;
error = pdf_load_jpx_image(&tile, xref, dict);
if (error)
return fz_rethrow(error, "cannot load jpx image");
if (forcemask)
{
if (tile->n != 2)
{
fz_drop_pixmap(tile);
return fz_throw("softmask must be grayscale");
}
mask = fz_alpha_from_gray(tile, 1);
fz_drop_pixmap(tile);
*imgp = mask;
return fz_okay;
}
*imgp = tile;
return fz_okay;
}
 
w = fz_to_int(fz_dict_getsa(dict, "Width", "W"));
h = fz_to_int(fz_dict_getsa(dict, "Height", "H"));
bpc = fz_to_int(fz_dict_getsa(dict, "BitsPerComponent", "BPC"));
imagemask = fz_to_bool(fz_dict_getsa(dict, "ImageMask", "IM"));
interpolate = fz_to_bool(fz_dict_getsa(dict, "Interpolate", "I"));
 
indexed = 0;
usecolorkey = 0;
colorspace = NULL;
mask = NULL;
 
if (imagemask)
bpc = 1;
 
if (w == 0)
return fz_throw("image width is zero");
if (h == 0)
return fz_throw("image height is zero");
if (bpc == 0)
return fz_throw("image depth is zero");
if (bpc > 16)
return fz_throw("image depth is too large: %d", bpc);
if (w > (1 << 16))
return fz_throw("image is too wide");
if (h > (1 << 16))
return fz_throw("image is too high");
 
obj = fz_dict_getsa(dict, "ColorSpace", "CS");
if (obj && !imagemask && !forcemask)
{
/* colorspace resource lookup is only done for inline images */
if (fz_is_name(obj))
{
res = fz_dict_get(fz_dict_gets(rdb, "ColorSpace"), obj);
if (res)
obj = res;
}
 
error = pdf_load_colorspace(&colorspace, xref, obj);
if (error)
return fz_rethrow(error, "cannot load image colorspace");
 
if (!strcmp(colorspace->name, "Indexed"))
indexed = 1;
 
n = colorspace->n;
}
else
{
n = 1;
}
 
obj = fz_dict_getsa(dict, "Decode", "D");
if (obj)
{
for (i = 0; i < n * 2; i++)
decode[i] = fz_to_real(fz_array_get(obj, i));
}
else
{
float maxval = indexed ? (1 << bpc) - 1 : 1;
for (i = 0; i < n * 2; i++)
decode[i] = i & 1 ? maxval : 0;
}
 
obj = fz_dict_getsa(dict, "SMask", "Mask");
if (fz_is_dict(obj))
{
/* Not allowed for inline images */
if (!cstm)
{
error = pdf_load_image_imp(&mask, xref, rdb, obj, NULL, 1);
if (error)
{
if (colorspace)
fz_drop_colorspace(colorspace);
return fz_rethrow(error, "cannot load image mask/softmask");
}
}
}
else if (fz_is_array(obj))
{
usecolorkey = 1;
for (i = 0; i < n * 2; i++)
colorkey[i] = fz_to_int(fz_array_get(obj, i));
}
 
/* Allocate now, to fail early if we run out of memory */
tile = fz_new_pixmap_with_limit(colorspace, w, h);
if (!tile)
{
if (colorspace)
fz_drop_colorspace(colorspace);
if (mask)
fz_drop_pixmap(mask);
return fz_throw("out of memory");
}
 
if (colorspace)
fz_drop_colorspace(colorspace);
 
tile->mask = mask;
tile->interpolate = interpolate;
 
stride = (w * n * bpc + 7) / 8;
 
if (cstm)
{
stm = pdf_open_inline_stream(cstm, xref, dict, stride * h);
}
else
{
error = pdf_open_stream(&stm, xref, fz_to_num(dict), fz_to_gen(dict));
if (error)
{
fz_drop_pixmap(tile);
return fz_rethrow(error, "cannot open image data stream (%d 0 R)", fz_to_num(dict));
}
}
 
samples = fz_calloc(h, stride);
 
len = fz_read(stm, samples, h * stride);
if (len < 0)
{
fz_close(stm);
fz_free(samples);
fz_drop_pixmap(tile);
return fz_rethrow(len, "cannot read image data");
}
 
/* Make sure we read the EOF marker (for inline images only) */
if (cstm)
{
unsigned char tbuf[512];
int tlen = fz_read(stm, tbuf, sizeof tbuf);
if (tlen < 0)
fz_catch(tlen, "ignoring error at end of image");
if (tlen > 0)
fz_warn("ignoring garbage at end of image");
}
 
fz_close(stm);
 
/* Pad truncated images */
if (len < stride * h)
{
fz_warn("padding truncated image (%d 0 R)", fz_to_num(dict));
memset(samples + len, 0, stride * h - len);
}
 
/* Invert 1-bit image masks */
if (imagemask)
{
/* 0=opaque and 1=transparent so we need to invert */
unsigned char *p = samples;
len = h * stride;
for (i = 0; i < len; i++)
p[i] = ~p[i];
}
 
fz_unpack_tile(tile, samples, n, bpc, stride, indexed);
 
fz_free(samples);
 
if (usecolorkey)
pdf_mask_color_key(tile, n, colorkey);
 
if (indexed)
{
fz_pixmap *conv;
fz_decode_indexed_tile(tile, decode, (1 << bpc) - 1);
conv = pdf_expand_indexed_pixmap(tile);
fz_drop_pixmap(tile);
tile = conv;
}
else
{
fz_decode_tile(tile, decode);
}
 
*imgp = tile;
return fz_okay;
}
 
fz_error
pdf_load_inline_image(fz_pixmap **pixp, pdf_xref *xref, fz_obj *rdb, fz_obj *dict, fz_stream *file)
{
fz_error error;
 
error = pdf_load_image_imp(pixp, xref, rdb, dict, file, 0);
if (error)
return fz_rethrow(error, "cannot load inline image");
 
return fz_okay;
}
 
int
pdf_is_jpx_image(fz_obj *dict)
{
fz_obj *filter;
int i;
 
filter = fz_dict_gets(dict, "Filter");
if (!strcmp(fz_to_name(filter), "JPXDecode"))
return 1;
for (i = 0; i < fz_array_len(filter); i++)
if (!strcmp(fz_to_name(fz_array_get(filter, i)), "JPXDecode"))
return 1;
return 0;
}
 
static fz_error
pdf_load_jpx_image(fz_pixmap **imgp, pdf_xref *xref, fz_obj *dict)
{
fz_error error;
fz_buffer *buf;
fz_colorspace *colorspace;
fz_pixmap *img;
fz_obj *obj;
 
colorspace = NULL;
 
error = pdf_load_stream(&buf, xref, fz_to_num(dict), fz_to_gen(dict));
if (error)
return fz_rethrow(error, "cannot load jpx image data");
 
obj = fz_dict_gets(dict, "ColorSpace");
if (obj)
{
error = pdf_load_colorspace(&colorspace, xref, obj);
if (error)
fz_catch(error, "cannot load image colorspace");
}
 
error = fz_load_jpx_image(&img, buf->data, buf->len, colorspace);
if (error)
{
if (colorspace)
fz_drop_colorspace(colorspace);
fz_drop_buffer(buf);
return fz_rethrow(error, "cannot load jpx image");
}
 
if (colorspace)
fz_drop_colorspace(colorspace);
fz_drop_buffer(buf);
 
obj = fz_dict_getsa(dict, "SMask", "Mask");
if (fz_is_dict(obj))
{
error = pdf_load_image_imp(&img->mask, xref, NULL, obj, NULL, 1);
if (error)
{
fz_drop_pixmap(img);
return fz_rethrow(error, "cannot load image mask/softmask");
}
}
 
*imgp = img;
return fz_okay;
}
 
fz_error
pdf_load_image(fz_pixmap **pixp, pdf_xref *xref, fz_obj *dict)
{
fz_error error;
 
if ((*pixp = pdf_find_item(xref->store, fz_drop_pixmap, dict)))
{
fz_keep_pixmap(*pixp);
return fz_okay;
}
 
error = pdf_load_image_imp(pixp, xref, NULL, dict, NULL, 0);
if (error)
return fz_rethrow(error, "cannot load image (%d 0 R)", fz_to_num(dict));
 
pdf_store_item(xref->store, fz_keep_pixmap, fz_drop_pixmap, dict, *pixp);
 
return fz_okay;
}
/contrib/media/updf/pdf/pdf_interpret.c
0,0 → 1,2304
#include "fitz.h"
#include "mupdf.h"
 
#define TILE
 
typedef struct pdf_material_s pdf_material;
typedef struct pdf_gstate_s pdf_gstate;
typedef struct pdf_csi_s pdf_csi;
 
enum
{
PDF_FILL,
PDF_STROKE,
};
 
enum
{
PDF_MAT_NONE,
PDF_MAT_COLOR,
PDF_MAT_PATTERN,
PDF_MAT_SHADE,
};
 
struct pdf_material_s
{
int kind;
fz_colorspace *colorspace;
pdf_pattern *pattern;
fz_shade *shade;
float alpha;
float v[32];
};
 
struct pdf_gstate_s
{
fz_matrix ctm;
int clip_depth;
 
/* path stroking */
fz_stroke_state stroke_state;
 
/* materials */
pdf_material stroke;
pdf_material fill;
 
/* text state */
float char_space;
float word_space;
float scale;
float leading;
pdf_font_desc *font;
float size;
int render;
float rise;
 
/* transparency */
int blendmode;
pdf_xobject *softmask;
fz_matrix softmask_ctm;
float softmask_bc[FZ_MAX_COLORS];
int luminosity;
};
 
struct pdf_csi_s
{
fz_device *dev;
pdf_xref *xref;
 
/* usage mode for optional content groups */
char *target; /* "View", "Print", "Export" */
 
/* interpreter stack */
fz_obj *obj;
char name[256];
unsigned char string[256];
int string_len;
float stack[32];
int top;
 
int xbalance;
int in_text;
 
/* path object state */
fz_path *path;
 
/* text object state */
fz_text *text;
fz_matrix tlm;
fz_matrix tm;
int text_mode;
int accumulate;
 
/* graphics state */
fz_matrix top_ctm;
pdf_gstate gstate[64];
int gtop;
};
 
static fz_error pdf_run_buffer(pdf_csi *csi, fz_obj *rdb, fz_buffer *contents);
static fz_error pdf_run_xobject(pdf_csi *csi, fz_obj *resources, pdf_xobject *xobj, fz_matrix transform);
static void pdf_show_pattern(pdf_csi *csi, pdf_pattern *pat, fz_rect area, int what);
 
 
static int
pdf_is_hidden_ocg(fz_obj *xobj, char *target)
{
char target_state[16];
fz_obj *obj;
 
fz_strlcpy(target_state, target, sizeof target_state);
fz_strlcat(target_state, "State", sizeof target_state);
 
obj = fz_dict_gets(xobj, "OC");
obj = fz_dict_gets(obj, "OCGs");
if (fz_is_array(obj))
obj = fz_array_get(obj, 0);
obj = fz_dict_gets(obj, "Usage");
obj = fz_dict_gets(obj, target);
obj = fz_dict_gets(obj, target_state);
return !strcmp(fz_to_name(obj), "OFF");
}
 
/*
* Emit graphics calls to device.
*/
 
static void
pdf_begin_group(pdf_csi *csi, fz_rect bbox)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
fz_error error;
 
if (gstate->softmask)
{
pdf_xobject *softmask = gstate->softmask;
fz_rect bbox = fz_transform_rect(gstate->softmask_ctm, softmask->bbox);
fz_matrix save_ctm = gstate->ctm;
 
gstate->softmask = NULL;
gstate->ctm = gstate->softmask_ctm;
 
fz_begin_mask(csi->dev, bbox, gstate->luminosity,
softmask->colorspace, gstate->softmask_bc);
error = pdf_run_xobject(csi, NULL, softmask, fz_identity);
if (error)
fz_catch(error, "cannot run softmask");
fz_end_mask(csi->dev);
 
gstate->softmask = softmask;
gstate->ctm = save_ctm;
}
 
if (gstate->blendmode)
fz_begin_group(csi->dev, bbox, 1, 0, gstate->blendmode, 1);
}
 
static void
pdf_end_group(pdf_csi *csi)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
 
if (gstate->blendmode)
fz_end_group(csi->dev);
 
if (gstate->softmask)
fz_pop_clip(csi->dev);
}
 
static void
pdf_show_shade(pdf_csi *csi, fz_shade *shd)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
fz_rect bbox;
 
bbox = fz_bound_shade(shd, gstate->ctm);
 
pdf_begin_group(csi, bbox);
 
fz_fill_shade(csi->dev, shd, gstate->ctm, gstate->fill.alpha);
 
pdf_end_group(csi);
}
 
static void
pdf_show_image(pdf_csi *csi, fz_pixmap *image)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
fz_rect bbox;
 
bbox = fz_transform_rect(gstate->ctm, fz_unit_rect);
 
if (image->mask)
{
/* apply blend group even though we skip the softmask */
if (gstate->blendmode)
fz_begin_group(csi->dev, bbox, 0, 0, gstate->blendmode, 1);
fz_clip_image_mask(csi->dev, image->mask, &bbox, gstate->ctm);
}
else
pdf_begin_group(csi, bbox);
 
if (!image->colorspace)
{
 
switch (gstate->fill.kind)
{
case PDF_MAT_NONE:
break;
case PDF_MAT_COLOR:
fz_fill_image_mask(csi->dev, image, gstate->ctm,
gstate->fill.colorspace, gstate->fill.v, gstate->fill.alpha);
break;
case PDF_MAT_PATTERN:
if (gstate->fill.pattern)
{
fz_clip_image_mask(csi->dev, image, &bbox, gstate->ctm);
pdf_show_pattern(csi, gstate->fill.pattern, bbox, PDF_FILL);
fz_pop_clip(csi->dev);
}
break;
case PDF_MAT_SHADE:
if (gstate->fill.shade)
{
fz_clip_image_mask(csi->dev, image, &bbox, gstate->ctm);
fz_fill_shade(csi->dev, gstate->fill.shade, gstate->ctm, gstate->fill.alpha);
fz_pop_clip(csi->dev);
}
break;
}
}
else
{
fz_fill_image(csi->dev, image, gstate->ctm, gstate->fill.alpha);
}
 
if (image->mask)
{
fz_pop_clip(csi->dev);
if (gstate->blendmode)
fz_end_group(csi->dev);
}
else
pdf_end_group(csi);
}
 
static void pdf_show_clip(pdf_csi *csi, int even_odd)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
 
gstate->clip_depth++;
fz_clip_path(csi->dev, csi->path, NULL, even_odd, gstate->ctm);
}
 
static void
pdf_show_path(pdf_csi *csi, int doclose, int dofill, int dostroke, int even_odd)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
fz_path *path;
fz_rect bbox;
 
path = csi->path;
csi->path = fz_new_path();
 
if (doclose)
fz_closepath(path);
 
if (dostroke)
bbox = fz_bound_path(path, &gstate->stroke_state, gstate->ctm);
else
bbox = fz_bound_path(path, NULL, gstate->ctm);
 
if (dofill || dostroke)
pdf_begin_group(csi, bbox);
 
if (dofill)
{
switch (gstate->fill.kind)
{
case PDF_MAT_NONE:
break;
case PDF_MAT_COLOR:
fz_fill_path(csi->dev, path, even_odd, gstate->ctm,
gstate->fill.colorspace, gstate->fill.v, gstate->fill.alpha);
break;
case PDF_MAT_PATTERN:
if (gstate->fill.pattern)
{
fz_clip_path(csi->dev, path, NULL, even_odd, gstate->ctm);
pdf_show_pattern(csi, gstate->fill.pattern, bbox, PDF_FILL);
fz_pop_clip(csi->dev);
}
break;
case PDF_MAT_SHADE:
if (gstate->fill.shade)
{
fz_clip_path(csi->dev, path, NULL, even_odd, gstate->ctm);
fz_fill_shade(csi->dev, gstate->fill.shade, csi->top_ctm, gstate->fill.alpha);
fz_pop_clip(csi->dev);
}
break;
}
}
 
if (dostroke)
{
switch (gstate->stroke.kind)
{
case PDF_MAT_NONE:
break;
case PDF_MAT_COLOR:
fz_stroke_path(csi->dev, path, &gstate->stroke_state, gstate->ctm,
gstate->stroke.colorspace, gstate->stroke.v, gstate->stroke.alpha);
break;
case PDF_MAT_PATTERN:
if (gstate->stroke.pattern)
{
fz_clip_stroke_path(csi->dev, path, &bbox, &gstate->stroke_state, gstate->ctm);
pdf_show_pattern(csi, gstate->stroke.pattern, bbox, PDF_FILL);
fz_pop_clip(csi->dev);
}
break;
case PDF_MAT_SHADE:
if (gstate->stroke.shade)
{
fz_clip_stroke_path(csi->dev, path, &bbox, &gstate->stroke_state, gstate->ctm);
fz_fill_shade(csi->dev, gstate->stroke.shade, csi->top_ctm, gstate->stroke.alpha);
fz_pop_clip(csi->dev);
}
break;
}
}
 
if (dofill || dostroke)
pdf_end_group(csi);
 
fz_free_path(path);
}
 
/*
* Assemble and emit text
*/
 
static void
pdf_flush_text(pdf_csi *csi)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
fz_text *text;
int dofill = 0;
int dostroke = 0;
int doclip = 0;
int doinvisible = 0;
fz_rect bbox;
 
if (!csi->text)
return;
text = csi->text;
csi->text = NULL;
 
dofill = dostroke = doclip = doinvisible = 0;
switch (csi->text_mode)
{
case 0: dofill = 1; break;
case 1: dostroke = 1; break;
case 2: dofill = dostroke = 1; break;
case 3: doinvisible = 1; break;
case 4: dofill = doclip = 1; break;
case 5: dostroke = doclip = 1; break;
case 6: dofill = dostroke = doclip = 1; break;
case 7: doclip = 1; break;
}
 
bbox = fz_bound_text(text, gstate->ctm);
 
pdf_begin_group(csi, bbox);
 
if (doinvisible)
fz_ignore_text(csi->dev, text, gstate->ctm);
 
if (doclip)
{
if (csi->accumulate < 2)
gstate->clip_depth++;
fz_clip_text(csi->dev, text, gstate->ctm, csi->accumulate);
csi->accumulate = 2;
}
 
if (dofill)
{
switch (gstate->fill.kind)
{
case PDF_MAT_NONE:
break;
case PDF_MAT_COLOR:
fz_fill_text(csi->dev, text, gstate->ctm,
gstate->fill.colorspace, gstate->fill.v, gstate->fill.alpha);
break;
case PDF_MAT_PATTERN:
if (gstate->fill.pattern)
{
fz_clip_text(csi->dev, text, gstate->ctm, 0);
pdf_show_pattern(csi, gstate->fill.pattern, bbox, PDF_FILL);
fz_pop_clip(csi->dev);
}
break;
case PDF_MAT_SHADE:
if (gstate->fill.shade)
{
fz_clip_text(csi->dev, text, gstate->ctm, 0);
fz_fill_shade(csi->dev, gstate->fill.shade, csi->top_ctm, gstate->fill.alpha);
fz_pop_clip(csi->dev);
}
break;
}
}
 
if (dostroke)
{
switch (gstate->stroke.kind)
{
case PDF_MAT_NONE:
break;
case PDF_MAT_COLOR:
fz_stroke_text(csi->dev, text, &gstate->stroke_state, gstate->ctm,
gstate->stroke.colorspace, gstate->stroke.v, gstate->stroke.alpha);
break;
case PDF_MAT_PATTERN:
if (gstate->stroke.pattern)
{
fz_clip_stroke_text(csi->dev, text, &gstate->stroke_state, gstate->ctm);
pdf_show_pattern(csi, gstate->stroke.pattern, bbox, PDF_FILL);
fz_pop_clip(csi->dev);
}
break;
case PDF_MAT_SHADE:
if (gstate->stroke.shade)
{
fz_clip_stroke_text(csi->dev, text, &gstate->stroke_state, gstate->ctm);
fz_fill_shade(csi->dev, gstate->stroke.shade, csi->top_ctm, gstate->stroke.alpha);
fz_pop_clip(csi->dev);
}
break;
}
}
 
pdf_end_group(csi);
 
fz_free_text(text);
}
 
static void
pdf_show_char(pdf_csi *csi, int cid)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
pdf_font_desc *fontdesc = gstate->font;
fz_matrix tsm, trm;
float w0, w1, tx, ty;
pdf_hmtx h;
pdf_vmtx v;
int gid;
int ucsbuf[8];
int ucslen;
int i;
 
tsm.a = gstate->size * gstate->scale;
tsm.b = 0;
tsm.c = 0;
tsm.d = gstate->size;
tsm.e = 0;
tsm.f = gstate->rise;
 
ucslen = 0;
if (fontdesc->to_unicode)
ucslen = pdf_lookup_cmap_full(fontdesc->to_unicode, cid, ucsbuf);
if (ucslen == 0 && cid < fontdesc->cid_to_ucs_len)
{
ucsbuf[0] = fontdesc->cid_to_ucs[cid];
ucslen = 1;
}
if (ucslen == 0 || (ucslen == 1 && ucsbuf[0] == 0))
{
ucsbuf[0] = '?';
ucslen = 1;
}
 
gid = pdf_font_cid_to_gid(fontdesc, cid);
 
if (fontdesc->wmode == 1)
{
v = pdf_get_vmtx(fontdesc, cid);
tsm.e -= v.x * gstate->size * 0.001f;
tsm.f -= v.y * gstate->size * 0.001f;
}
 
trm = fz_concat(tsm, csi->tm);
 
/* flush buffered text if face or matrix or rendermode has changed */
if (!csi->text ||
fontdesc->font != csi->text->font ||
fontdesc->wmode != csi->text->wmode ||
fabsf(trm.a - csi->text->trm.a) > FLT_EPSILON ||
fabsf(trm.b - csi->text->trm.b) > FLT_EPSILON ||
fabsf(trm.c - csi->text->trm.c) > FLT_EPSILON ||
fabsf(trm.d - csi->text->trm.d) > FLT_EPSILON ||
gstate->render != csi->text_mode)
{
pdf_flush_text(csi);
 
csi->text = fz_new_text(fontdesc->font, trm, fontdesc->wmode);
csi->text->trm.e = 0;
csi->text->trm.f = 0;
csi->text_mode = gstate->render;
}
 
/* add glyph to textobject */
fz_add_text(csi->text, gid, ucsbuf[0], trm.e, trm.f);
 
/* add filler glyphs for one-to-many unicode mapping */
for (i = 1; i < ucslen; i++)
fz_add_text(csi->text, -1, ucsbuf[i], trm.e, trm.f);
 
if (fontdesc->wmode == 0)
{
h = pdf_get_hmtx(fontdesc, cid);
w0 = h.w * 0.001f;
tx = (w0 * gstate->size + gstate->char_space) * gstate->scale;
csi->tm = fz_concat(fz_translate(tx, 0), csi->tm);
}
 
if (fontdesc->wmode == 1)
{
w1 = v.w * 0.001f;
ty = w1 * gstate->size + gstate->char_space;
csi->tm = fz_concat(fz_translate(0, ty), csi->tm);
}
}
 
static void
pdf_show_space(pdf_csi *csi, float tadj)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
pdf_font_desc *fontdesc = gstate->font;
 
if (!fontdesc)
{
fz_warn("cannot draw text since font and size not set");
return;
}
 
if (fontdesc->wmode == 0)
csi->tm = fz_concat(fz_translate(tadj * gstate->scale, 0), csi->tm);
else
csi->tm = fz_concat(fz_translate(0, tadj), csi->tm);
}
 
static void
pdf_show_string(pdf_csi *csi, unsigned char *buf, int len)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
pdf_font_desc *fontdesc = gstate->font;
unsigned char *end = buf + len;
int cpt, cid;
 
if (!fontdesc)
{
fz_warn("cannot draw text since font and size not set");
return;
}
 
while (buf < end)
{
buf = pdf_decode_cmap(fontdesc->encoding, buf, &cpt);
cid = pdf_lookup_cmap(fontdesc->encoding, cpt);
if (cid >= 0)
pdf_show_char(csi, cid);
else
fz_warn("cannot encode character with code point %#x", cpt);
if (cpt == 32)
pdf_show_space(csi, gstate->word_space);
}
}
 
static void
pdf_show_text(pdf_csi *csi, fz_obj *text)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
int i;
 
if (fz_is_array(text))
{
for (i = 0; i < fz_array_len(text); i++)
{
fz_obj *item = fz_array_get(text, i);
if (fz_is_string(item))
pdf_show_string(csi, (unsigned char *)fz_to_str_buf(item), fz_to_str_len(item));
else
pdf_show_space(csi, - fz_to_real(item) * gstate->size * 0.001f);
}
}
else if (fz_is_string(text))
{
pdf_show_string(csi, (unsigned char *)fz_to_str_buf(text), fz_to_str_len(text));
}
}
 
/*
* Interpreter and graphics state stack.
*/
 
static void
pdf_init_gstate(pdf_gstate *gs, fz_matrix ctm)
{
gs->ctm = ctm;
gs->clip_depth = 0;
 
gs->stroke_state.start_cap = 0;
gs->stroke_state.dash_cap = 0;
gs->stroke_state.end_cap = 0;
gs->stroke_state.linejoin = 0;
gs->stroke_state.linewidth = 1;
gs->stroke_state.miterlimit = 10;
gs->stroke_state.dash_phase = 0;
gs->stroke_state.dash_len = 0;
memset(gs->stroke_state.dash_list, 0, sizeof(gs->stroke_state.dash_list));
 
gs->stroke.kind = PDF_MAT_COLOR;
gs->stroke.colorspace = fz_keep_colorspace(fz_device_gray);
gs->stroke.v[0] = 0;
gs->stroke.pattern = NULL;
gs->stroke.shade = NULL;
gs->stroke.alpha = 1;
 
gs->fill.kind = PDF_MAT_COLOR;
gs->fill.colorspace = fz_keep_colorspace(fz_device_gray);
gs->fill.v[0] = 0;
gs->fill.pattern = NULL;
gs->fill.shade = NULL;
gs->fill.alpha = 1;
 
gs->char_space = 0;
gs->word_space = 0;
gs->scale = 1;
gs->leading = 0;
gs->font = NULL;
gs->size = -1;
gs->render = 0;
gs->rise = 0;
 
gs->blendmode = 0;
gs->softmask = NULL;
gs->softmask_ctm = fz_identity;
gs->luminosity = 0;
}
 
static pdf_csi *
pdf_new_csi(pdf_xref *xref, fz_device *dev, fz_matrix ctm, char *target)
{
pdf_csi *csi;
 
csi = fz_malloc(sizeof(pdf_csi));
csi->xref = xref;
csi->dev = dev;
csi->target = target;
 
csi->top = 0;
csi->obj = NULL;
csi->name[0] = 0;
csi->string_len = 0;
memset(csi->stack, 0, sizeof csi->stack);
 
csi->xbalance = 0;
csi->in_text = 0;
 
csi->path = fz_new_path();
 
csi->text = NULL;
csi->tlm = fz_identity;
csi->tm = fz_identity;
csi->text_mode = 0;
csi->accumulate = 1;
 
csi->top_ctm = ctm;
pdf_init_gstate(&csi->gstate[0], ctm);
csi->gtop = 0;
 
return csi;
}
 
static void
pdf_clear_stack(pdf_csi *csi)
{
int i;
 
if (csi->obj)
fz_drop_obj(csi->obj);
csi->obj = NULL;
 
csi->name[0] = 0;
csi->string_len = 0;
for (i = 0; i < csi->top; i++)
csi->stack[i] = 0;
 
csi->top = 0;
}
 
static pdf_material *
pdf_keep_material(pdf_material *mat)
{
if (mat->colorspace)
fz_keep_colorspace(mat->colorspace);
if (mat->pattern)
pdf_keep_pattern(mat->pattern);
if (mat->shade)
fz_keep_shade(mat->shade);
return mat;
}
 
static pdf_material *
pdf_drop_material(pdf_material *mat)
{
if (mat->colorspace)
fz_drop_colorspace(mat->colorspace);
if (mat->pattern)
pdf_drop_pattern(mat->pattern);
if (mat->shade)
fz_drop_shade(mat->shade);
return mat;
}
 
static void
pdf_gsave(pdf_csi *csi)
{
pdf_gstate *gs = csi->gstate + csi->gtop;
 
if (csi->gtop == nelem(csi->gstate) - 1)
{
fz_warn("gstate overflow in content stream");
return;
}
 
memcpy(&csi->gstate[csi->gtop + 1], &csi->gstate[csi->gtop], sizeof(pdf_gstate));
 
csi->gtop ++;
 
pdf_keep_material(&gs->stroke);
pdf_keep_material(&gs->fill);
if (gs->font)
pdf_keep_font(gs->font);
if (gs->softmask)
pdf_keep_xobject(gs->softmask);
}
 
static void
pdf_grestore(pdf_csi *csi)
{
pdf_gstate *gs = csi->gstate + csi->gtop;
int clip_depth = gs->clip_depth;
 
if (csi->gtop == 0)
{
fz_warn("gstate underflow in content stream");
return;
}
 
pdf_drop_material(&gs->stroke);
pdf_drop_material(&gs->fill);
if (gs->font)
pdf_drop_font(gs->font);
if (gs->softmask)
pdf_drop_xobject(gs->softmask);
 
csi->gtop --;
 
gs = csi->gstate + csi->gtop;
while (clip_depth > gs->clip_depth)
{
fz_pop_clip(csi->dev);
clip_depth--;
}
}
 
static void
pdf_free_csi(pdf_csi *csi)
{
while (csi->gtop)
pdf_grestore(csi);
 
pdf_drop_material(&csi->gstate[0].fill);
pdf_drop_material(&csi->gstate[0].stroke);
if (csi->gstate[0].font)
pdf_drop_font(csi->gstate[0].font);
if (csi->gstate[0].softmask)
pdf_drop_xobject(csi->gstate[0].softmask);
 
while (csi->gstate[0].clip_depth--)
fz_pop_clip(csi->dev);
 
if (csi->path) fz_free_path(csi->path);
if (csi->text) fz_free_text(csi->text);
 
pdf_clear_stack(csi);
 
fz_free(csi);
}
 
/*
* Material state
*/
 
static void
pdf_set_colorspace(pdf_csi *csi, int what, fz_colorspace *colorspace)
{
pdf_gstate *gs = csi->gstate + csi->gtop;
pdf_material *mat;
 
pdf_flush_text(csi);
 
mat = what == PDF_FILL ? &gs->fill : &gs->stroke;
 
fz_drop_colorspace(mat->colorspace);
 
mat->kind = PDF_MAT_COLOR;
mat->colorspace = fz_keep_colorspace(colorspace);
 
mat->v[0] = 0;
mat->v[1] = 0;
mat->v[2] = 0;
mat->v[3] = 1;
}
 
static void
pdf_set_color(pdf_csi *csi, int what, float *v)
{
pdf_gstate *gs = csi->gstate + csi->gtop;
pdf_material *mat;
int i;
 
pdf_flush_text(csi);
 
mat = what == PDF_FILL ? &gs->fill : &gs->stroke;
 
switch (mat->kind)
{
case PDF_MAT_PATTERN:
case PDF_MAT_COLOR:
if (!strcmp(mat->colorspace->name, "Lab"))
{
mat->v[0] = v[0] / 100;
mat->v[1] = (v[1] + 100) / 200;
mat->v[2] = (v[2] + 100) / 200;
}
for (i = 0; i < mat->colorspace->n; i++)
mat->v[i] = v[i];
break;
default:
fz_warn("color incompatible with material");
}
}
 
static void
pdf_set_shade(pdf_csi *csi, int what, fz_shade *shade)
{
pdf_gstate *gs = csi->gstate + csi->gtop;
pdf_material *mat;
 
pdf_flush_text(csi);
 
mat = what == PDF_FILL ? &gs->fill : &gs->stroke;
 
if (mat->shade)
fz_drop_shade(mat->shade);
 
mat->kind = PDF_MAT_SHADE;
mat->shade = fz_keep_shade(shade);
}
 
static void
pdf_set_pattern(pdf_csi *csi, int what, pdf_pattern *pat, float *v)
{
pdf_gstate *gs = csi->gstate + csi->gtop;
pdf_material *mat;
 
pdf_flush_text(csi);
 
mat = what == PDF_FILL ? &gs->fill : &gs->stroke;
 
if (mat->pattern)
pdf_drop_pattern(mat->pattern);
 
mat->kind = PDF_MAT_PATTERN;
if (pat)
mat->pattern = pdf_keep_pattern(pat);
else
mat->pattern = NULL;
 
if (v)
pdf_set_color(csi, what, v);
}
 
static void
pdf_unset_pattern(pdf_csi *csi, int what)
{
pdf_gstate *gs = csi->gstate + csi->gtop;
pdf_material *mat;
mat = what == PDF_FILL ? &gs->fill : &gs->stroke;
if (mat->kind == PDF_MAT_PATTERN)
{
if (mat->pattern)
pdf_drop_pattern(mat->pattern);
mat->pattern = NULL;
mat->kind = PDF_MAT_COLOR;
}
}
 
/*
* Patterns, XObjects and ExtGState
*/
 
static void
pdf_show_pattern(pdf_csi *csi, pdf_pattern *pat, fz_rect area, int what)
{
pdf_gstate *gstate;
fz_matrix ptm, invptm;
fz_matrix oldtopctm;
fz_error error;
int x0, y0, x1, y1;
int oldtop;
 
pdf_gsave(csi);
gstate = csi->gstate + csi->gtop;
 
if (pat->ismask)
{
pdf_unset_pattern(csi, PDF_FILL);
pdf_unset_pattern(csi, PDF_STROKE);
if (what == PDF_FILL)
{
pdf_drop_material(&gstate->stroke);
pdf_keep_material(&gstate->fill);
gstate->stroke = gstate->fill;
}
if (what == PDF_STROKE)
{
pdf_drop_material(&gstate->fill);
pdf_keep_material(&gstate->stroke);
gstate->fill = gstate->stroke;
}
}
else
{
// TODO: unset only the current fill/stroke or both?
pdf_unset_pattern(csi, what);
}
 
/* don't apply softmasks to objects in the pattern as well */
if (gstate->softmask)
{
pdf_drop_xobject(gstate->softmask);
gstate->softmask = NULL;
}
 
ptm = fz_concat(pat->matrix, csi->top_ctm);
invptm = fz_invert_matrix(ptm);
 
/* patterns are painted using the ctm in effect at the beginning of the content stream */
/* get bbox of shape in pattern space for stamping */
area = fz_transform_rect(invptm, area);
x0 = floorf(area.x0 / pat->xstep);
y0 = floorf(area.y0 / pat->ystep);
x1 = ceilf(area.x1 / pat->xstep);
y1 = ceilf(area.y1 / pat->ystep);
 
oldtopctm = csi->top_ctm;
oldtop = csi->gtop;
 
#ifdef TILE
if ((x1 - x0) * (y1 - y0) > 0)
{
fz_begin_tile(csi->dev, area, pat->bbox, pat->xstep, pat->ystep, ptm);
gstate->ctm = ptm;
csi->top_ctm = gstate->ctm;
pdf_gsave(csi);
error = pdf_run_buffer(csi, pat->resources, pat->contents);
if (error)
fz_catch(error, "cannot render pattern tile");
pdf_grestore(csi);
while (oldtop < csi->gtop)
pdf_grestore(csi);
fz_end_tile(csi->dev);
}
#else
{
int x, y;
for (y = y0; y < y1; y++)
{
for (x = x0; x < x1; x++)
{
gstate->ctm = fz_concat(fz_translate(x * pat->xstep, y * pat->ystep), ptm);
csi->top_ctm = gstate->ctm;
error = pdf_run_csi_buffer(csi, pat->resources, pat->contents);
while (oldtop < csi->gtop)
pdf_grestore(csi);
if (error)
{
fz_catch(error, "cannot render pattern tile");
goto cleanup;
}
}
}
}
cleanup:
#endif
 
csi->top_ctm = oldtopctm;
 
pdf_grestore(csi);
}
 
static fz_error
pdf_run_xobject(pdf_csi *csi, fz_obj *resources, pdf_xobject *xobj, fz_matrix transform)
{
fz_error error;
pdf_gstate *gstate;
fz_matrix oldtopctm;
int oldtop;
int popmask;
 
pdf_gsave(csi);
 
gstate = csi->gstate + csi->gtop;
oldtop = csi->gtop;
popmask = 0;
 
/* apply xobject's transform matrix */
transform = fz_concat(xobj->matrix, transform);
gstate->ctm = fz_concat(transform, gstate->ctm);
 
/* apply soft mask, create transparency group and reset state */
if (xobj->transparency)
{
if (gstate->softmask)
{
pdf_xobject *softmask = gstate->softmask;
fz_rect bbox = fz_transform_rect(gstate->ctm, xobj->bbox);
 
gstate->softmask = NULL;
popmask = 1;
 
fz_begin_mask(csi->dev, bbox, gstate->luminosity,
softmask->colorspace, gstate->softmask_bc);
error = pdf_run_xobject(csi, resources, softmask, fz_identity);
if (error)
return fz_rethrow(error, "cannot run softmask");
fz_end_mask(csi->dev);
 
pdf_drop_xobject(softmask);
}
 
fz_begin_group(csi->dev,
fz_transform_rect(gstate->ctm, xobj->bbox),
xobj->isolated, xobj->knockout, gstate->blendmode, gstate->fill.alpha);
 
gstate->blendmode = 0;
gstate->stroke.alpha = 1;
gstate->fill.alpha = 1;
}
 
/* clip to the bounds */
 
fz_moveto(csi->path, xobj->bbox.x0, xobj->bbox.y0);
fz_lineto(csi->path, xobj->bbox.x1, xobj->bbox.y0);
fz_lineto(csi->path, xobj->bbox.x1, xobj->bbox.y1);
fz_lineto(csi->path, xobj->bbox.x0, xobj->bbox.y1);
fz_closepath(csi->path);
pdf_show_clip(csi, 0);
pdf_show_path(csi, 0, 0, 0, 0);
 
/* run contents */
 
oldtopctm = csi->top_ctm;
csi->top_ctm = gstate->ctm;
 
if (xobj->resources)
resources = xobj->resources;
 
error = pdf_run_buffer(csi, resources, xobj->contents);
if (error)
fz_catch(error, "cannot interpret XObject stream");
 
csi->top_ctm = oldtopctm;
 
while (oldtop < csi->gtop)
pdf_grestore(csi);
 
pdf_grestore(csi);
 
/* wrap up transparency stacks */
 
if (xobj->transparency)
{
fz_end_group(csi->dev);
if (popmask)
fz_pop_clip(csi->dev);
}
 
return fz_okay;
}
 
static fz_error
pdf_run_extgstate(pdf_csi *csi, fz_obj *rdb, fz_obj *extgstate)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
fz_colorspace *colorspace;
int i, k;
 
pdf_flush_text(csi);
 
for (i = 0; i < fz_dict_len(extgstate); i++)
{
fz_obj *key = fz_dict_get_key(extgstate, i);
fz_obj *val = fz_dict_get_val(extgstate, i);
char *s = fz_to_name(key);
 
if (!strcmp(s, "Font"))
{
if (fz_is_array(val) && fz_array_len(val) == 2)
{
fz_error error;
fz_obj *font = fz_array_get(val, 0);
 
if (gstate->font)
{
pdf_drop_font(gstate->font);
gstate->font = NULL;
}
 
error = pdf_load_font(&gstate->font, csi->xref, rdb, font);
if (error)
return fz_rethrow(error, "cannot load font (%d %d R)", fz_to_num(font), fz_to_gen(font));
if (!gstate->font)
return fz_throw("cannot find font in store");
gstate->size = fz_to_real(fz_array_get(val, 1));
}
else
return fz_throw("malformed /Font dictionary");
}
 
else if (!strcmp(s, "LC"))
{
gstate->stroke_state.start_cap = fz_to_int(val);
gstate->stroke_state.dash_cap = fz_to_int(val);
gstate->stroke_state.end_cap = fz_to_int(val);
}
else if (!strcmp(s, "LW"))
gstate->stroke_state.linewidth = fz_to_real(val);
else if (!strcmp(s, "LJ"))
gstate->stroke_state.linejoin = fz_to_int(val);
else if (!strcmp(s, "ML"))
gstate->stroke_state.miterlimit = fz_to_real(val);
 
else if (!strcmp(s, "D"))
{
if (fz_is_array(val) && fz_array_len(val) == 2)
{
fz_obj *dashes = fz_array_get(val, 0);
gstate->stroke_state.dash_len = MAX(fz_array_len(dashes), 32);
for (k = 0; k < gstate->stroke_state.dash_len; k++)
gstate->stroke_state.dash_list[k] = fz_to_real(fz_array_get(dashes, k));
gstate->stroke_state.dash_phase = fz_to_real(fz_array_get(val, 1));
}
else
return fz_throw("malformed /D");
}
 
else if (!strcmp(s, "CA"))
gstate->stroke.alpha = fz_to_real(val);
 
else if (!strcmp(s, "ca"))
gstate->fill.alpha = fz_to_real(val);
 
else if (!strcmp(s, "BM"))
{
if (fz_is_array(val))
val = fz_array_get(val, 0);
gstate->blendmode = fz_find_blendmode(fz_to_name(val));
}
 
else if (!strcmp(s, "SMask"))
{
if (fz_is_dict(val))
{
fz_error error;
pdf_xobject *xobj;
fz_obj *group, *luminosity, *bc;
 
if (gstate->softmask)
{
pdf_drop_xobject(gstate->softmask);
gstate->softmask = NULL;
}
 
group = fz_dict_gets(val, "G");
if (!group)
return fz_throw("cannot load softmask xobject (%d %d R)", fz_to_num(val), fz_to_gen(val));
error = pdf_load_xobject(&xobj, csi->xref, group);
if (error)
return fz_rethrow(error, "cannot load xobject (%d %d R)", fz_to_num(val), fz_to_gen(val));
 
colorspace = xobj->colorspace;
if (!colorspace)
colorspace = fz_device_gray;
 
gstate->softmask_ctm = fz_concat(xobj->matrix, gstate->ctm);
gstate->softmask = xobj;
for (k = 0; k < colorspace->n; k++)
gstate->softmask_bc[k] = 0;
 
bc = fz_dict_gets(val, "BC");
if (fz_is_array(bc))
{
for (k = 0; k < colorspace->n; k++)
gstate->softmask_bc[k] = fz_to_real(fz_array_get(bc, k));
}
 
luminosity = fz_dict_gets(val, "S");
if (fz_is_name(luminosity) && !strcmp(fz_to_name(luminosity), "Luminosity"))
gstate->luminosity = 1;
else
gstate->luminosity = 0;
}
else if (fz_is_name(val) && !strcmp(fz_to_name(val), "None"))
{
if (gstate->softmask)
{
pdf_drop_xobject(gstate->softmask);
gstate->softmask = NULL;
}
}
}
 
else if (!strcmp(s, "TR"))
{
if (fz_is_name(val) && strcmp(fz_to_name(val), "Identity"))
fz_warn("ignoring transfer function");
}
}
 
return fz_okay;
}
 
/*
* Operators
*/
 
static void pdf_run_BDC(pdf_csi *csi)
{
}
 
static fz_error pdf_run_BI(pdf_csi *csi, fz_obj *rdb, fz_stream *file)
{
int ch;
fz_error error;
char *buf = csi->xref->scratch;
int buflen = sizeof(csi->xref->scratch);
fz_pixmap *img;
fz_obj *obj;
 
error = pdf_parse_dict(&obj, csi->xref, file, buf, buflen);
if (error)
return fz_rethrow(error, "cannot parse inline image dictionary");
 
/* read whitespace after ID keyword */
ch = fz_read_byte(file);
if (ch == '\r')
if (fz_peek_byte(file) == '\n')
fz_read_byte(file);
 
error = pdf_load_inline_image(&img, csi->xref, rdb, obj, file);
fz_drop_obj(obj);
if (error)
return fz_rethrow(error, "cannot load inline image");
 
pdf_show_image(csi, img);
 
fz_drop_pixmap(img);
 
/* find EI */
ch = fz_read_byte(file);
while (ch != 'E' && ch != EOF)
ch = fz_read_byte(file);
ch = fz_read_byte(file);
if (ch != 'I')
return fz_rethrow(error, "syntax error after inline image");
 
return fz_okay;
}
 
static void pdf_run_B(pdf_csi *csi)
{
pdf_show_path(csi, 0, 1, 1, 0);
}
 
static void pdf_run_BMC(pdf_csi *csi)
{
}
 
static void pdf_run_BT(pdf_csi *csi)
{
csi->in_text = 1;
csi->tm = fz_identity;
csi->tlm = fz_identity;
}
 
static void pdf_run_BX(pdf_csi *csi)
{
csi->xbalance ++;
}
 
static void pdf_run_Bstar(pdf_csi *csi)
{
pdf_show_path(csi, 0, 1, 1, 1);
}
 
static fz_error pdf_run_cs_imp(pdf_csi *csi, fz_obj *rdb, int what)
{
fz_colorspace *colorspace;
fz_obj *obj, *dict;
fz_error error;
 
if (!strcmp(csi->name, "Pattern"))
{
pdf_set_pattern(csi, what, NULL, NULL);
}
else
{
if (!strcmp(csi->name, "DeviceGray"))
colorspace = fz_keep_colorspace(fz_device_gray);
else if (!strcmp(csi->name, "DeviceRGB"))
colorspace = fz_keep_colorspace(fz_device_rgb);
else if (!strcmp(csi->name, "DeviceCMYK"))
colorspace = fz_keep_colorspace(fz_device_cmyk);
else
{
dict = fz_dict_gets(rdb, "ColorSpace");
if (!dict)
return fz_throw("cannot find ColorSpace dictionary");
obj = fz_dict_gets(dict, csi->name);
if (!obj)
return fz_throw("cannot find colorspace resource '%s'", csi->name);
error = pdf_load_colorspace(&colorspace, csi->xref, obj);
if (error)
return fz_rethrow(error, "cannot load colorspace (%d 0 R)", fz_to_num(obj));
}
 
pdf_set_colorspace(csi, what, colorspace);
 
fz_drop_colorspace(colorspace);
}
return fz_okay;
}
 
static void pdf_run_CS(pdf_csi *csi, fz_obj *rdb)
{
fz_error error;
error = pdf_run_cs_imp(csi, rdb, PDF_STROKE);
if (error)
fz_catch(error, "cannot set colorspace");
}
 
static void pdf_run_cs(pdf_csi *csi, fz_obj *rdb)
{
fz_error error;
error = pdf_run_cs_imp(csi, rdb, PDF_FILL);
if (error)
fz_catch(error, "cannot set colorspace");
}
 
static void pdf_run_DP(pdf_csi *csi)
{
}
 
static fz_error pdf_run_Do(pdf_csi *csi, fz_obj *rdb)
{
fz_obj *dict;
fz_obj *obj;
fz_obj *subtype;
fz_error error;
 
dict = fz_dict_gets(rdb, "XObject");
if (!dict)
return fz_throw("cannot find XObject dictionary when looking for: '%s'", csi->name);
 
obj = fz_dict_gets(dict, csi->name);
if (!obj)
return fz_throw("cannot find xobject resource: '%s'", csi->name);
 
subtype = fz_dict_gets(obj, "Subtype");
if (!fz_is_name(subtype))
return fz_throw("no XObject subtype specified");
 
if (pdf_is_hidden_ocg(obj, csi->target))
return fz_okay;
 
if (!strcmp(fz_to_name(subtype), "Form") && fz_dict_gets(obj, "Subtype2"))
subtype = fz_dict_gets(obj, "Subtype2");
 
if (!strcmp(fz_to_name(subtype), "Form"))
{
pdf_xobject *xobj;
 
error = pdf_load_xobject(&xobj, csi->xref, obj);
if (error)
return fz_rethrow(error, "cannot load xobject (%d %d R)", fz_to_num(obj), fz_to_gen(obj));
 
/* Inherit parent resources, in case this one was empty XXX check where it's loaded */
if (!xobj->resources)
xobj->resources = fz_keep_obj(rdb);
 
error = pdf_run_xobject(csi, xobj->resources, xobj, fz_identity);
if (error)
return fz_rethrow(error, "cannot draw xobject (%d %d R)", fz_to_num(obj), fz_to_gen(obj));
 
pdf_drop_xobject(xobj);
}
 
else if (!strcmp(fz_to_name(subtype), "Image"))
{
if ((csi->dev->hints & FZ_IGNORE_IMAGE) == 0)
{
fz_pixmap *img;
error = pdf_load_image(&img, csi->xref, obj);
if (error)
return fz_rethrow(error, "cannot load image (%d %d R)", fz_to_num(obj), fz_to_gen(obj));
pdf_show_image(csi, img);
fz_drop_pixmap(img);
}
}
 
else if (!strcmp(fz_to_name(subtype), "PS"))
{
fz_warn("ignoring XObject with subtype PS");
}
 
else
{
return fz_throw("unknown XObject subtype: '%s'", fz_to_name(subtype));
}
 
return fz_okay;
}
 
static void pdf_run_EMC(pdf_csi *csi)
{
}
 
static void pdf_run_ET(pdf_csi *csi)
{
pdf_flush_text(csi);
csi->accumulate = 1;
csi->in_text = 0;
}
 
static void pdf_run_EX(pdf_csi *csi)
{
csi->xbalance --;
}
 
static void pdf_run_F(pdf_csi *csi)
{
pdf_show_path(csi, 0, 1, 0, 0);
}
 
static void pdf_run_G(pdf_csi *csi)
{
pdf_set_colorspace(csi, PDF_STROKE, fz_device_gray);
pdf_set_color(csi, PDF_STROKE, csi->stack);
}
 
static void pdf_run_J(pdf_csi *csi)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
gstate->stroke_state.start_cap = csi->stack[0];
gstate->stroke_state.dash_cap = csi->stack[0];
gstate->stroke_state.end_cap = csi->stack[0];
}
 
static void pdf_run_K(pdf_csi *csi)
{
pdf_set_colorspace(csi, PDF_STROKE, fz_device_cmyk);
pdf_set_color(csi, PDF_STROKE, csi->stack);
}
 
static void pdf_run_M(pdf_csi *csi)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
gstate->stroke_state.miterlimit = csi->stack[0];
}
 
static void pdf_run_MP(pdf_csi *csi)
{
}
 
static void pdf_run_Q(pdf_csi *csi)
{
pdf_grestore(csi);
}
 
static void pdf_run_RG(pdf_csi *csi)
{
pdf_set_colorspace(csi, PDF_STROKE, fz_device_rgb);
pdf_set_color(csi, PDF_STROKE, csi->stack);
}
 
static void pdf_run_S(pdf_csi *csi)
{
pdf_show_path(csi, 0, 0, 1, 0);
}
 
static fz_error pdf_run_SC_imp(pdf_csi *csi, fz_obj *rdb, int what, pdf_material *mat)
{
fz_error error;
fz_obj *patterntype;
fz_obj *dict;
fz_obj *obj;
int kind;
 
kind = mat->kind;
if (csi->name[0])
kind = PDF_MAT_PATTERN;
 
switch (kind)
{
case PDF_MAT_NONE:
return fz_throw("cannot set color in mask objects");
 
case PDF_MAT_COLOR:
pdf_set_color(csi, what, csi->stack);
break;
 
case PDF_MAT_PATTERN:
dict = fz_dict_gets(rdb, "Pattern");
if (!dict)
return fz_throw("cannot find Pattern dictionary");
 
obj = fz_dict_gets(dict, csi->name);
if (!obj)
return fz_throw("cannot find pattern resource '%s'", csi->name);
 
patterntype = fz_dict_gets(obj, "PatternType");
 
if (fz_to_int(patterntype) == 1)
{
pdf_pattern *pat;
error = pdf_load_pattern(&pat, csi->xref, obj);
if (error)
return fz_rethrow(error, "cannot load pattern (%d 0 R)", fz_to_num(obj));
pdf_set_pattern(csi, what, pat, csi->top > 0 ? csi->stack : NULL);
pdf_drop_pattern(pat);
}
else if (fz_to_int(patterntype) == 2)
{
fz_shade *shd;
error = pdf_load_shading(&shd, csi->xref, obj);
if (error)
return fz_rethrow(error, "cannot load shading (%d 0 R)", fz_to_num(obj));
pdf_set_shade(csi, what, shd);
fz_drop_shade(shd);
}
else
{
return fz_throw("unknown pattern type: %d", fz_to_int(patterntype));
}
break;
 
case PDF_MAT_SHADE:
return fz_throw("cannot set color in shade objects");
}
 
return fz_okay;
}
 
static void pdf_run_SC(pdf_csi *csi, fz_obj *rdb)
{
fz_error error;
pdf_gstate *gstate = csi->gstate + csi->gtop;
error = pdf_run_SC_imp(csi, rdb, PDF_STROKE, &gstate->stroke);
if (error)
fz_catch(error, "cannot set color and colorspace");
}
 
static void pdf_run_sc(pdf_csi *csi, fz_obj *rdb)
{
fz_error error;
pdf_gstate *gstate = csi->gstate + csi->gtop;
error = pdf_run_SC_imp(csi, rdb, PDF_FILL, &gstate->fill);
if (error)
fz_catch(error, "cannot set color and colorspace");
}
 
static void pdf_run_Tc(pdf_csi *csi)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
gstate->char_space = csi->stack[0];
}
 
static void pdf_run_Tw(pdf_csi *csi)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
gstate->word_space = csi->stack[0];
}
 
static void pdf_run_Tz(pdf_csi *csi)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
float a = csi->stack[0] / 100;
pdf_flush_text(csi);
gstate->scale = a;
}
 
static void pdf_run_TL(pdf_csi *csi)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
gstate->leading = csi->stack[0];
}
 
static fz_error pdf_run_Tf(pdf_csi *csi, fz_obj *rdb)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
fz_error error;
fz_obj *dict;
fz_obj *obj;
 
gstate->size = csi->stack[0];
if (gstate->font)
pdf_drop_font(gstate->font);
gstate->font = NULL;
 
dict = fz_dict_gets(rdb, "Font");
if (!dict)
return fz_throw("cannot find Font dictionary");
 
obj = fz_dict_gets(dict, csi->name);
if (!obj)
return fz_throw("cannot find font resource: '%s'", csi->name);
 
error = pdf_load_font(&gstate->font, csi->xref, rdb, obj);
if (error)
return fz_rethrow(error, "cannot load font (%d 0 R)", fz_to_num(obj));
 
return fz_okay;
}
 
static void pdf_run_Tr(pdf_csi *csi)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
gstate->render = csi->stack[0];
}
 
static void pdf_run_Ts(pdf_csi *csi)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
gstate->rise = csi->stack[0];
}
 
static void pdf_run_Td(pdf_csi *csi)
{
fz_matrix m = fz_translate(csi->stack[0], csi->stack[1]);
csi->tlm = fz_concat(m, csi->tlm);
csi->tm = csi->tlm;
}
 
static void pdf_run_TD(pdf_csi *csi)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
fz_matrix m;
 
gstate->leading = -csi->stack[1];
m = fz_translate(csi->stack[0], csi->stack[1]);
csi->tlm = fz_concat(m, csi->tlm);
csi->tm = csi->tlm;
}
 
static void pdf_run_Tm(pdf_csi *csi)
{
csi->tm.a = csi->stack[0];
csi->tm.b = csi->stack[1];
csi->tm.c = csi->stack[2];
csi->tm.d = csi->stack[3];
csi->tm.e = csi->stack[4];
csi->tm.f = csi->stack[5];
csi->tlm = csi->tm;
}
 
static void pdf_run_Tstar(pdf_csi *csi)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
fz_matrix m = fz_translate(0, -gstate->leading);
csi->tlm = fz_concat(m, csi->tlm);
csi->tm = csi->tlm;
}
 
static void pdf_run_Tj(pdf_csi *csi)
{
if (csi->string_len)
pdf_show_string(csi, csi->string, csi->string_len);
else
pdf_show_text(csi, csi->obj);
}
 
static void pdf_run_TJ(pdf_csi *csi)
{
if (csi->string_len)
pdf_show_string(csi, csi->string, csi->string_len);
else
pdf_show_text(csi, csi->obj);
}
 
static void pdf_run_W(pdf_csi *csi)
{
pdf_show_clip(csi, 0);
}
 
static void pdf_run_Wstar(pdf_csi *csi)
{
pdf_show_clip(csi, 1);
}
 
static void pdf_run_b(pdf_csi *csi)
{
pdf_show_path(csi, 1, 1, 1, 0);
}
 
static void pdf_run_bstar(pdf_csi *csi)
{
pdf_show_path(csi, 1, 1, 1, 1);
}
 
static void pdf_run_c(pdf_csi *csi)
{
float a, b, c, d, e, f;
a = csi->stack[0];
b = csi->stack[1];
c = csi->stack[2];
d = csi->stack[3];
e = csi->stack[4];
f = csi->stack[5];
fz_curveto(csi->path, a, b, c, d, e, f);
}
 
static void pdf_run_cm(pdf_csi *csi)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
fz_matrix m;
 
m.a = csi->stack[0];
m.b = csi->stack[1];
m.c = csi->stack[2];
m.d = csi->stack[3];
m.e = csi->stack[4];
m.f = csi->stack[5];
 
gstate->ctm = fz_concat(m, gstate->ctm);
}
 
static void pdf_run_d(pdf_csi *csi)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
fz_obj *array;
int i;
 
array = csi->obj;
gstate->stroke_state.dash_len = MIN(fz_array_len(array), nelem(gstate->stroke_state.dash_list));
for (i = 0; i < gstate->stroke_state.dash_len; i++)
gstate->stroke_state.dash_list[i] = fz_to_real(fz_array_get(array, i));
gstate->stroke_state.dash_phase = csi->stack[0];
}
 
static void pdf_run_d0(pdf_csi *csi)
{
csi->dev->flags |= FZ_CHARPROC_COLOR;
}
 
static void pdf_run_d1(pdf_csi *csi)
{
csi->dev->flags |= FZ_CHARPROC_MASK;
}
 
static void pdf_run_f(pdf_csi *csi)
{
pdf_show_path(csi, 0, 1, 0, 0);
}
 
static void pdf_run_fstar(pdf_csi *csi)
{
pdf_show_path(csi, 0, 1, 0, 1);
}
 
static void pdf_run_g(pdf_csi *csi)
{
pdf_set_colorspace(csi, PDF_FILL, fz_device_gray);
pdf_set_color(csi, PDF_FILL, csi->stack);
}
 
static fz_error pdf_run_gs(pdf_csi *csi, fz_obj *rdb)
{
fz_error error;
fz_obj *dict;
fz_obj *obj;
 
dict = fz_dict_gets(rdb, "ExtGState");
if (!dict)
return fz_throw("cannot find ExtGState dictionary");
 
obj = fz_dict_gets(dict, csi->name);
if (!obj)
return fz_throw("cannot find extgstate resource '%s'", csi->name);
 
error = pdf_run_extgstate(csi, rdb, obj);
if (error)
return fz_rethrow(error, "cannot set ExtGState (%d 0 R)", fz_to_num(obj));
return fz_okay;
}
 
static void pdf_run_h(pdf_csi *csi)
{
fz_closepath(csi->path);
}
 
static void pdf_run_i(pdf_csi *csi)
{
}
 
static void pdf_run_j(pdf_csi *csi)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
gstate->stroke_state.linejoin = csi->stack[0];
}
 
static void pdf_run_k(pdf_csi *csi)
{
pdf_set_colorspace(csi, PDF_FILL, fz_device_cmyk);
pdf_set_color(csi, PDF_FILL, csi->stack);
}
 
static void pdf_run_l(pdf_csi *csi)
{
float a, b;
a = csi->stack[0];
b = csi->stack[1];
fz_lineto(csi->path, a, b);
}
 
static void pdf_run_m(pdf_csi *csi)
{
float a, b;
a = csi->stack[0];
b = csi->stack[1];
fz_moveto(csi->path, a, b);
}
 
static void pdf_run_n(pdf_csi *csi)
{
pdf_show_path(csi, 0, 0, 0, 0);
}
 
static void pdf_run_q(pdf_csi *csi)
{
pdf_gsave(csi);
}
 
static void pdf_run_re(pdf_csi *csi)
{
float x, y, w, h;
 
x = csi->stack[0];
y = csi->stack[1];
w = csi->stack[2];
h = csi->stack[3];
 
fz_moveto(csi->path, x, y);
fz_lineto(csi->path, x + w, y);
fz_lineto(csi->path, x + w, y + h);
fz_lineto(csi->path, x, y + h);
fz_closepath(csi->path);
}
 
static void pdf_run_rg(pdf_csi *csi)
{
pdf_set_colorspace(csi, PDF_FILL, fz_device_rgb);
pdf_set_color(csi, PDF_FILL, csi->stack);
}
 
static void pdf_run_ri(pdf_csi *csi)
{
}
 
static void pdf_run(pdf_csi *csi)
{
pdf_show_path(csi, 1, 0, 1, 0);
}
 
static fz_error pdf_run_sh(pdf_csi *csi, fz_obj *rdb)
{
fz_obj *dict;
fz_obj *obj;
fz_shade *shd;
fz_error error;
 
dict = fz_dict_gets(rdb, "Shading");
if (!dict)
return fz_throw("cannot find shading dictionary");
 
obj = fz_dict_gets(dict, csi->name);
if (!obj)
return fz_throw("cannot find shading resource: '%s'", csi->name);
 
if ((csi->dev->hints & FZ_IGNORE_SHADE) == 0)
{
error = pdf_load_shading(&shd, csi->xref, obj);
if (error)
return fz_rethrow(error, "cannot load shading (%d %d R)", fz_to_num(obj), fz_to_gen(obj));
pdf_show_shade(csi, shd);
fz_drop_shade(shd);
}
return fz_okay;
}
 
static void pdf_run_v(pdf_csi *csi)
{
float a, b, c, d;
a = csi->stack[0];
b = csi->stack[1];
c = csi->stack[2];
d = csi->stack[3];
fz_curvetov(csi->path, a, b, c, d);
}
 
static void pdf_run_w(pdf_csi *csi)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
pdf_flush_text(csi); /* linewidth affects stroked text rendering mode */
gstate->stroke_state.linewidth = csi->stack[0];
}
 
static void pdf_run_y(pdf_csi *csi)
{
float a, b, c, d;
a = csi->stack[0];
b = csi->stack[1];
c = csi->stack[2];
d = csi->stack[3];
fz_curvetoy(csi->path, a, b, c, d);
}
 
static void pdf_run_squote(pdf_csi *csi)
{
fz_matrix m;
pdf_gstate *gstate = csi->gstate + csi->gtop;
 
m = fz_translate(0, -gstate->leading);
csi->tlm = fz_concat(m, csi->tlm);
csi->tm = csi->tlm;
 
if (csi->string_len)
pdf_show_string(csi, csi->string, csi->string_len);
else
pdf_show_text(csi, csi->obj);
}
 
static void pdf_run_dquote(pdf_csi *csi)
{
fz_matrix m;
pdf_gstate *gstate = csi->gstate + csi->gtop;
 
gstate->word_space = csi->stack[0];
gstate->char_space = csi->stack[1];
 
m = fz_translate(0, -gstate->leading);
csi->tlm = fz_concat(m, csi->tlm);
csi->tm = csi->tlm;
 
if (csi->string_len)
pdf_show_string(csi, csi->string, csi->string_len);
else
pdf_show_text(csi, csi->obj);
}
 
#define A(a) (a)
#define B(a,b) (a | b << 8)
#define C(a,b,c) (a | b << 8 | c << 16)
 
static fz_error
pdf_run_keyword(pdf_csi *csi, fz_obj *rdb, fz_stream *file, char *buf)
{
fz_error error;
int key;
 
key = buf[0];
if (buf[1])
{
key |= buf[1] << 8;
if (buf[2])
{
key |= buf[2] << 16;
if (buf[3])
key = 0;
}
}
 
switch (key)
{
case A('"'): pdf_run_dquote(csi); break;
case A('\''): pdf_run_squote(csi); break;
case A('B'): pdf_run_B(csi); break;
case B('B','*'): pdf_run_Bstar(csi); break;
case C('B','D','C'): pdf_run_BDC(csi); break;
case B('B','I'):
error = pdf_run_BI(csi, rdb, file);
if (error)
return fz_rethrow(error, "cannot draw inline image");
break;
case C('B','M','C'): pdf_run_BMC(csi); break;
case B('B','T'): pdf_run_BT(csi); break;
case B('B','X'): pdf_run_BX(csi); break;
case B('C','S'): pdf_run_CS(csi, rdb); break;
case B('D','P'): pdf_run_DP(csi); break;
case B('D','o'):
error = pdf_run_Do(csi, rdb);
if (error)
fz_catch(error, "cannot draw xobject/image");
break;
case C('E','M','C'): pdf_run_EMC(csi); break;
case B('E','T'): pdf_run_ET(csi); break;
case B('E','X'): pdf_run_EX(csi); break;
case A('F'): pdf_run_F(csi); break;
case A('G'): pdf_run_G(csi); break;
case A('J'): pdf_run_J(csi); break;
case A('K'): pdf_run_K(csi); break;
case A('M'): pdf_run_M(csi); break;
case B('M','P'): pdf_run_MP(csi); break;
case A('Q'): pdf_run_Q(csi); break;
case B('R','G'): pdf_run_RG(csi); break;
case A('S'): pdf_run_S(csi); break;
case B('S','C'): pdf_run_SC(csi, rdb); break;
case C('S','C','N'): pdf_run_SC(csi, rdb); break;
case B('T','*'): pdf_run_Tstar(csi); break;
case B('T','D'): pdf_run_TD(csi); break;
case B('T','J'): pdf_run_TJ(csi); break;
case B('T','L'): pdf_run_TL(csi); break;
case B('T','c'): pdf_run_Tc(csi); break;
case B('T','d'): pdf_run_Td(csi); break;
case B('T','f'):
error = pdf_run_Tf(csi, rdb);
if (error)
fz_catch(error, "cannot set font");
break;
case B('T','j'): pdf_run_Tj(csi); break;
case B('T','m'): pdf_run_Tm(csi); break;
case B('T','r'): pdf_run_Tr(csi); break;
case B('T','s'): pdf_run_Ts(csi); break;
case B('T','w'): pdf_run_Tw(csi); break;
case B('T','z'): pdf_run_Tz(csi); break;
case A('W'): pdf_run_W(csi); break;
case B('W','*'): pdf_run_Wstar(csi); break;
case A('b'): pdf_run_b(csi); break;
case B('b','*'): pdf_run_bstar(csi); break;
case A('c'): pdf_run_c(csi); break;
case B('c','m'): pdf_run_cm(csi); break;
case B('c','s'): pdf_run_cs(csi, rdb); break;
case A('d'): pdf_run_d(csi); break;
case B('d','0'): pdf_run_d0(csi); break;
case B('d','1'): pdf_run_d1(csi); break;
case A('f'): pdf_run_f(csi); break;
case B('f','*'): pdf_run_fstar(csi); break;
case A('g'): pdf_run_g(csi); break;
case B('g','s'):
error = pdf_run_gs(csi, rdb);
if (error)
fz_catch(error, "cannot set graphics state");
break;
case A('h'): pdf_run_h(csi); break;
case A('i'): pdf_run_i(csi); break;
case A('j'): pdf_run_j(csi); break;
case A('k'): pdf_run_k(csi); break;
case A('l'): pdf_run_l(csi); break;
case A('m'): pdf_run_m(csi); break;
case A('n'): pdf_run_n(csi); break;
case A('q'): pdf_run_q(csi); break;
case B('r','e'): pdf_run_re(csi); break;
case B('r','g'): pdf_run_rg(csi); break;
case B('r','i'): pdf_run_ri(csi); break;
case A('s'): pdf_run(csi); break;
case B('s','c'): pdf_run_sc(csi, rdb); break;
case C('s','c','n'): pdf_run_sc(csi, rdb); break;
case B('s','h'):
error = pdf_run_sh(csi, rdb);
if (error)
fz_catch(error, "cannot draw shading");
break;
case A('v'): pdf_run_v(csi); break;
case A('w'): pdf_run_w(csi); break;
case A('y'): pdf_run_y(csi); break;
default:
if (!csi->xbalance)
fz_warn("unknown keyword: '%s'", buf);
break;
}
 
return fz_okay;
}
 
static fz_error
pdf_run_stream(pdf_csi *csi, fz_obj *rdb, fz_stream *file, char *buf, int buflen)
{
fz_error error;
int tok, len, in_array;
 
/* make sure we have a clean slate if we come here from flush_text */
pdf_clear_stack(csi);
in_array = 0;
 
while (1)
{
if (csi->top == nelem(csi->stack) - 1)
return fz_throw("stack overflow");
 
error = pdf_lex(&tok, file, buf, buflen, &len);
if (error)
return fz_rethrow(error, "lexical error in content stream");
 
if (in_array)
{
if (tok == PDF_TOK_CLOSE_ARRAY)
{
in_array = 0;
}
else if (tok == PDF_TOK_INT || tok == PDF_TOK_REAL)
{
pdf_gstate *gstate = csi->gstate + csi->gtop;
pdf_show_space(csi, -fz_atof(buf) * gstate->size * 0.001f);
}
else if (tok == PDF_TOK_STRING)
{
pdf_show_string(csi, (unsigned char *)buf, len);
}
else if (tok == PDF_TOK_KEYWORD)
{
if (!strcmp(buf, "Tw") || !strcmp(buf, "Tc"))
fz_warn("ignoring keyword '%s' inside array", buf);
else
return fz_throw("syntax error in array");
}
else if (tok == PDF_TOK_EOF)
return fz_okay;
else
return fz_throw("syntax error in array");
}
 
else switch (tok)
{
case PDF_TOK_ENDSTREAM:
case PDF_TOK_EOF:
return fz_okay;
 
case PDF_TOK_OPEN_ARRAY:
if (!csi->in_text)
{
error = pdf_parse_array(&csi->obj, csi->xref, file, buf, buflen);
if (error)
return fz_rethrow(error, "cannot parse array");
}
else
{
in_array = 1;
}
break;
 
case PDF_TOK_OPEN_DICT:
error = pdf_parse_dict(&csi->obj, csi->xref, file, buf, buflen);
if (error)
return fz_rethrow(error, "cannot parse dictionary");
break;
 
case PDF_TOK_NAME:
fz_strlcpy(csi->name, buf, sizeof(csi->name));
break;
 
case PDF_TOK_INT:
csi->stack[csi->top] = atoi(buf);
csi->top ++;
break;
 
case PDF_TOK_REAL:
csi->stack[csi->top] = fz_atof(buf);
csi->top ++;
break;
 
case PDF_TOK_STRING:
if (len <= sizeof(csi->string))
{
memcpy(csi->string, buf, len);
csi->string_len = len;
}
else
{
csi->obj = fz_new_string(buf, len);
}
break;
 
case PDF_TOK_KEYWORD:
error = pdf_run_keyword(csi, rdb, file, buf);
if (error)
return fz_rethrow(error, "cannot run keyword");
pdf_clear_stack(csi);
break;
 
default:
return fz_throw("syntax error in content stream");
}
}
}
 
/*
* Entry points
*/
 
static fz_error
pdf_run_buffer(pdf_csi *csi, fz_obj *rdb, fz_buffer *contents)
{
fz_error error;
int len = sizeof csi->xref->scratch;
char *buf = fz_malloc(len); /* we must be re-entrant for type3 fonts */
fz_stream *file = fz_open_buffer(contents);
int save_in_text = csi->in_text;
csi->in_text = 0;
error = pdf_run_stream(csi, rdb, file, buf, len);
csi->in_text = save_in_text;
fz_close(file);
fz_free(buf);
if (error)
return fz_rethrow(error, "cannot parse content stream");
return fz_okay;
}
 
fz_error
pdf_run_page_with_usage(pdf_xref *xref, pdf_page *page, fz_device *dev, fz_matrix ctm, char *target)
{
pdf_csi *csi;
fz_error error;
pdf_annot *annot;
int flags;
 
if (page->transparency)
fz_begin_group(dev, fz_transform_rect(ctm, page->mediabox), 1, 0, 0, 1);
 
csi = pdf_new_csi(xref, dev, ctm, target);
error = pdf_run_buffer(csi, page->resources, page->contents);
pdf_free_csi(csi);
if (error)
return fz_rethrow(error, "cannot parse page content stream");
 
for (annot = page->annots; annot; annot = annot->next)
{
flags = fz_to_int(fz_dict_gets(annot->obj, "F"));
 
/* TODO: NoZoom and NoRotate */
if (flags & (1 << 0)) /* Invisible */
continue;
if (flags & (1 << 1)) /* Hidden */
continue;
if (flags & (1 << 5)) /* NoView */
continue;
 
if (pdf_is_hidden_ocg(annot->obj, target))
continue;
 
csi = pdf_new_csi(xref, dev, ctm, target);
error = pdf_run_xobject(csi, page->resources, annot->ap, annot->matrix);
pdf_free_csi(csi);
if (error)
return fz_rethrow(error, "cannot parse annotation appearance stream");
}
 
if (page->transparency)
fz_end_group(dev);
 
return fz_okay;
}
 
fz_error
pdf_run_page(pdf_xref *xref, pdf_page *page, fz_device *dev, fz_matrix ctm)
{
return pdf_run_page_with_usage(xref, page, dev, ctm, "View");
}
 
fz_error
pdf_run_glyph(pdf_xref *xref, fz_obj *resources, fz_buffer *contents, fz_device *dev, fz_matrix ctm)
{
pdf_csi *csi = pdf_new_csi(xref, dev, ctm, "View");
fz_error error = pdf_run_buffer(csi, resources, contents);
pdf_free_csi(csi);
if (error)
return fz_rethrow(error, "cannot parse glyph content stream");
return fz_okay;
}
/contrib/media/updf/pdf/pdf_lex.c
0,0 → 1,461
#include "fitz.h"
#include "mupdf.h"
 
#define IS_NUMBER \
'+':case'-':case'.':case'0':case'1':case'2':case'3':\
case'4':case'5':case'6':case'7':case'8':case'9'
#define IS_WHITE \
'\000':case'\011':case'\012':case'\014':case'\015':case'\040'
#define IS_HEX \
'0':case'1':case'2':case'3':case'4':case'5':case'6':\
case'7':case'8':case'9':case'A':case'B':case'C':\
case'D':case'E':case'F':case'a':case'b':case'c':\
case'd':case'e':case'f'
#define IS_DELIM \
'(':case')':case'<':case'>':case'[':case']':case'{':\
case'}':case'/':case'%'
 
#define RANGE_0_9 \
'0':case'1':case'2':case'3':case'4':case'5':\
case'6':case'7':case'8':case'9'
#define RANGE_a_f \
'a':case'b':case'c':case'd':case'e':case'f'
#define RANGE_A_F \
'A':case'B':case'C':case'D':case'E':case'F'
 
static inline int iswhite(int ch)
{
return
ch == '\000' ||
ch == '\011' ||
ch == '\012' ||
ch == '\014' ||
ch == '\015' ||
ch == '\040';
}
 
static inline int unhex(int ch)
{
if (ch >= '0' && ch <= '9') return ch - '0';
if (ch >= 'A' && ch <= 'F') return ch - 'A' + 0xA;
if (ch >= 'a' && ch <= 'f') return ch - 'a' + 0xA;
return 0;
}
 
static void
lex_white(fz_stream *f)
{
int c;
do {
c = fz_read_byte(f);
} while ((c <= 32) && (iswhite(c)));
if (c != EOF)
fz_unread_byte(f);
}
 
static void
lex_comment(fz_stream *f)
{
int c;
do {
c = fz_read_byte(f);
} while ((c != '\012') && (c != '\015') && (c != EOF));
}
 
static int
lex_number(fz_stream *f, char *s, int n, int *tok)
{
char *buf = s;
*tok = PDF_TOK_INT;
 
/* Initially we might have +, -, . or a digit */
if (n > 1)
{
int c = fz_read_byte(f);
switch (c)
{
case '.':
*tok = PDF_TOK_REAL;
*s++ = c;
n--;
goto loop_after_dot;
case '+':
case '-':
case RANGE_0_9:
*s++ = c;
n--;
goto loop_after_sign;
default:
fz_unread_byte(f);
goto end;
case EOF:
goto end;
}
}
 
/* We can't accept a sign from here on in, just . or a digit */
loop_after_sign:
while (n > 1)
{
int c = fz_read_byte(f);
switch (c)
{
case '.':
*tok = PDF_TOK_REAL;
*s++ = c;
n--;
goto loop_after_dot;
case RANGE_0_9:
*s++ = c;
break;
default:
fz_unread_byte(f);
goto end;
case EOF:
goto end;
}
n--;
}
 
/* In here, we've seen a dot, so can accept just digits */
loop_after_dot:
while (n > 1)
{
int c = fz_read_byte(f);
switch (c)
{
case RANGE_0_9:
*s++ = c;
break;
default:
fz_unread_byte(f);
goto end;
case EOF:
goto end;
}
n--;
}
 
end:
*s = '\0';
return s-buf;
}
 
static void
lex_name(fz_stream *f, char *s, int n)
{
while (n > 1)
{
int c = fz_read_byte(f);
switch (c)
{
case IS_WHITE:
case IS_DELIM:
fz_unread_byte(f);
goto end;
case EOF:
goto end;
case '#':
{
int d;
c = fz_read_byte(f);
switch (c)
{
case RANGE_0_9:
d = (c - '0') << 4;
break;
case RANGE_a_f:
d = (c - 'a' + 10) << 4;
break;
case RANGE_A_F:
d = (c - 'A' + 10) << 4;
break;
default:
fz_unread_byte(f);
/* fallthrough */
case EOF:
goto end;
}
c = fz_read_byte(f);
switch (c)
{
case RANGE_0_9:
c -= '0';
break;
case RANGE_a_f:
c -= 'a' - 10;
break;
case RANGE_A_F:
c -= 'A' - 10;
break;
default:
fz_unread_byte(f);
/* fallthrough */
case EOF:
*s++ = d;
n--;
goto end;
}
*s++ = d + c;
n--;
break;
}
default:
*s++ = c;
n--;
break;
}
}
end:
*s = '\0';
}
 
static int
lex_string(fz_stream *f, char *buf, int n)
{
char *s = buf;
char *e = buf + n;
int bal = 1;
int oct;
int c;
 
while (s < e)
{
c = fz_read_byte(f);
switch (c)
{
case EOF:
goto end;
case '(':
bal++;
*s++ = c;
break;
case ')':
bal --;
if (bal == 0)
goto end;
*s++ = c;
break;
case '\\':
c = fz_read_byte(f);
switch (c)
{
case EOF:
goto end;
case 'n':
*s++ = '\n';
break;
case 'r':
*s++ = '\r';
break;
case 't':
*s++ = '\t';
break;
case 'b':
*s++ = '\b';
break;
case 'f':
*s++ = '\f';
break;
case '(':
*s++ = '(';
break;
case ')':
*s++ = ')';
break;
case '\\':
*s++ = '\\';
break;
case RANGE_0_9:
oct = c - '0';
c = fz_read_byte(f);
if (c >= '0' && c <= '9')
{
oct = oct * 8 + (c - '0');
c = fz_read_byte(f);
if (c >= '0' && c <= '9')
oct = oct * 8 + (c - '0');
else if (c != EOF)
fz_unread_byte(f);
}
else if (c != EOF)
fz_unread_byte(f);
*s++ = oct;
break;
case '\n':
break;
case '\r':
c = fz_read_byte(f);
if ((c != '\n') && (c != EOF))
fz_unread_byte(f);
break;
default:
*s++ = c;
}
break;
default:
*s++ = c;
break;
}
}
end:
return s - buf;
}
 
static int
lex_hex_string(fz_stream *f, char *buf, int n)
{
char *s = buf;
char *e = buf + n;
int a = 0, x = 0;
int c;
 
while (s < e)
{
c = fz_read_byte(f);
switch (c)
{
case IS_WHITE:
break;
case IS_HEX:
if (x)
{
*s++ = a * 16 + unhex(c);
x = !x;
}
else
{
a = unhex(c);
x = !x;
}
break;
case '>':
case EOF:
goto end;
default:
fz_warn("ignoring invalid character in hex string: '%c'", c);
}
}
end:
return s - buf;
}
 
static int
pdf_token_from_keyword(char *key)
{
switch (*key)
{
case 'R':
if (!strcmp(key, "R")) return PDF_TOK_R;
break;
case 't':
if (!strcmp(key, "true")) return PDF_TOK_TRUE;
if (!strcmp(key, "trailer")) return PDF_TOK_TRAILER;
break;
case 'f':
if (!strcmp(key, "false")) return PDF_TOK_FALSE;
break;
case 'n':
if (!strcmp(key, "null")) return PDF_TOK_NULL;
break;
case 'o':
if (!strcmp(key, "obj")) return PDF_TOK_OBJ;
break;
case 'e':
if (!strcmp(key, "endobj")) return PDF_TOK_ENDOBJ;
if (!strcmp(key, "endstream")) return PDF_TOK_ENDSTREAM;
break;
case 's':
if (!strcmp(key, "stream")) return PDF_TOK_STREAM;
if (!strcmp(key, "startxref")) return PDF_TOK_STARTXREF;
break;
case 'x':
if (!strcmp(key, "xref")) return PDF_TOK_XREF;
break;
default:
break;
}
 
return PDF_TOK_KEYWORD;
}
 
fz_error
pdf_lex(int *tok, fz_stream *f, char *buf, int n, int *sl)
{
while (1)
{
int c = fz_read_byte(f);
switch (c)
{
case EOF:
*tok = PDF_TOK_EOF;
return fz_okay;
case IS_WHITE:
lex_white(f);
break;
case '%':
lex_comment(f);
break;
case '/':
lex_name(f, buf, n);
*sl = strlen(buf);
*tok = PDF_TOK_NAME;
return fz_okay;
case '(':
*sl = lex_string(f, buf, n);
*tok = PDF_TOK_STRING;
return fz_okay;
case ')':
*tok = PDF_TOK_ERROR;
goto cleanuperror;
case '<':
c = fz_read_byte(f);
if (c == '<')
{
*tok = PDF_TOK_OPEN_DICT;
}
else
{
fz_unread_byte(f);
*sl = lex_hex_string(f, buf, n);
*tok = PDF_TOK_STRING;
}
return fz_okay;
case '>':
c = fz_read_byte(f);
if (c == '>')
{
*tok = PDF_TOK_CLOSE_DICT;
return fz_okay;
}
*tok = PDF_TOK_ERROR;
goto cleanuperror;
case '[':
*tok = PDF_TOK_OPEN_ARRAY;
return fz_okay;
case ']':
*tok = PDF_TOK_CLOSE_ARRAY;
return fz_okay;
case '{':
*tok = PDF_TOK_OPEN_BRACE;
return fz_okay;
case '}':
*tok = PDF_TOK_CLOSE_BRACE;
return fz_okay;
case IS_NUMBER:
fz_unread_byte(f);
*sl = lex_number(f, buf, n, tok);
return fz_okay;
default: /* isregular: !isdelim && !iswhite && c != EOF */
fz_unread_byte(f);
lex_name(f, buf, n);
*sl = strlen(buf);
*tok = pdf_token_from_keyword(buf);
return fz_okay;
}
}
 
cleanuperror:
*tok = PDF_TOK_ERROR;
return fz_throw("lexical error");
}
/contrib/media/updf/pdf/pdf_metrics.c
0,0 → 1,138
#include "fitz.h"
#include "mupdf.h"
 
void
pdf_set_font_wmode(pdf_font_desc *font, int wmode)
{
font->wmode = wmode;
}
 
void
pdf_set_default_hmtx(pdf_font_desc *font, int w)
{
font->dhmtx.w = w;
}
 
void
pdf_set_default_vmtx(pdf_font_desc *font, int y, int w)
{
font->dvmtx.y = y;
font->dvmtx.w = w;
}
 
void
pdf_add_hmtx(pdf_font_desc *font, int lo, int hi, int w)
{
if (font->hmtx_len + 1 >= font->hmtx_cap)
{
font->hmtx_cap = font->hmtx_cap + 16;
font->hmtx = fz_realloc(font->hmtx, font->hmtx_cap, sizeof(pdf_hmtx));
}
 
font->hmtx[font->hmtx_len].lo = lo;
font->hmtx[font->hmtx_len].hi = hi;
font->hmtx[font->hmtx_len].w = w;
font->hmtx_len++;
}
 
void
pdf_add_vmtx(pdf_font_desc *font, int lo, int hi, int x, int y, int w)
{
if (font->vmtx_len + 1 >= font->vmtx_cap)
{
font->vmtx_cap = font->vmtx_cap + 16;
font->vmtx = fz_realloc(font->vmtx, font->vmtx_cap, sizeof(pdf_vmtx));
}
 
font->vmtx[font->vmtx_len].lo = lo;
font->vmtx[font->vmtx_len].hi = hi;
font->vmtx[font->vmtx_len].x = x;
font->vmtx[font->vmtx_len].y = y;
font->vmtx[font->vmtx_len].w = w;
font->vmtx_len++;
}
 
static int cmph(const void *a0, const void *b0)
{
pdf_hmtx *a = (pdf_hmtx*)a0;
pdf_hmtx *b = (pdf_hmtx*)b0;
return a->lo - b->lo;
}
 
static int cmpv(const void *a0, const void *b0)
{
pdf_vmtx *a = (pdf_vmtx*)a0;
pdf_vmtx *b = (pdf_vmtx*)b0;
return a->lo - b->lo;
}
 
void
pdf_end_hmtx(pdf_font_desc *font)
{
if (!font->hmtx)
return;
qsort(font->hmtx, font->hmtx_len, sizeof(pdf_hmtx), cmph);
}
 
void
pdf_end_vmtx(pdf_font_desc *font)
{
if (!font->vmtx)
return;
qsort(font->vmtx, font->vmtx_len, sizeof(pdf_vmtx), cmpv);
}
 
pdf_hmtx
pdf_get_hmtx(pdf_font_desc *font, int cid)
{
int l = 0;
int r = font->hmtx_len - 1;
int m;
 
if (!font->hmtx)
goto notfound;
 
while (l <= r)
{
m = (l + r) >> 1;
if (cid < font->hmtx[m].lo)
r = m - 1;
else if (cid > font->hmtx[m].hi)
l = m + 1;
else
return font->hmtx[m];
}
 
notfound:
return font->dhmtx;
}
 
pdf_vmtx
pdf_get_vmtx(pdf_font_desc *font, int cid)
{
pdf_hmtx h;
pdf_vmtx v;
int l = 0;
int r = font->vmtx_len - 1;
int m;
 
if (!font->vmtx)
goto notfound;
 
while (l <= r)
{
m = (l + r) >> 1;
if (cid < font->vmtx[m].lo)
r = m - 1;
else if (cid > font->vmtx[m].hi)
l = m + 1;
else
return font->vmtx[m];
}
 
notfound:
h = pdf_get_hmtx(font, cid);
v = font->dvmtx;
v.x = h.w / 2;
return v;
}
/contrib/media/updf/pdf/pdf_nametree.c
0,0 → 1,139
#include "fitz.h"
#include "mupdf.h"
 
static fz_obj *
pdf_lookup_name_imp(fz_obj *node, fz_obj *needle)
{
fz_obj *kids = fz_dict_gets(node, "Kids");
fz_obj *names = fz_dict_gets(node, "Names");
 
if (fz_is_array(kids))
{
int l = 0;
int r = fz_array_len(kids) - 1;
 
while (l <= r)
{
int m = (l + r) >> 1;
fz_obj *kid = fz_array_get(kids, m);
fz_obj *limits = fz_dict_gets(kid, "Limits");
fz_obj *first = fz_array_get(limits, 0);
fz_obj *last = fz_array_get(limits, 1);
 
if (fz_objcmp(needle, first) < 0)
r = m - 1;
else if (fz_objcmp(needle, last) > 0)
l = m + 1;
else
return pdf_lookup_name_imp(kid, needle);
}
}
 
if (fz_is_array(names))
{
int l = 0;
int r = (fz_array_len(names) / 2) - 1;
 
while (l <= r)
{
int m = (l + r) >> 1;
int c;
fz_obj *key = fz_array_get(names, m * 2);
fz_obj *val = fz_array_get(names, m * 2 + 1);
 
c = fz_objcmp(needle, key);
if (c < 0)
r = m - 1;
else if (c > 0)
l = m + 1;
else
return val;
}
}
 
return NULL;
}
 
fz_obj *
pdf_lookup_name(pdf_xref *xref, char *which, fz_obj *needle)
{
fz_obj *root = fz_dict_gets(xref->trailer, "Root");
fz_obj *names = fz_dict_gets(root, "Names");
fz_obj *tree = fz_dict_gets(names, which);
return pdf_lookup_name_imp(tree, needle);
}
 
fz_obj *
pdf_lookup_dest(pdf_xref *xref, fz_obj *needle)
{
fz_obj *root = fz_dict_gets(xref->trailer, "Root");
fz_obj *dests = fz_dict_gets(root, "Dests");
fz_obj *names = fz_dict_gets(root, "Names");
fz_obj *dest = NULL;
 
/* PDF 1.1 has destinations in a dictionary */
if (dests)
{
if (fz_is_name(needle))
return fz_dict_get(dests, needle);
else
return fz_dict_gets(dests, fz_to_str_buf(needle));
}
 
/* PDF 1.2 has destinations in a name tree */
if (names && !dest)
{
fz_obj *tree = fz_dict_gets(names, "Dests");
return pdf_lookup_name_imp(tree, needle);
}
 
return NULL;
}
 
static void
pdf_load_name_tree_imp(fz_obj *dict, pdf_xref *xref, fz_obj *node)
{
fz_obj *kids = fz_dict_gets(node, "Kids");
fz_obj *names = fz_dict_gets(node, "Names");
int i;
 
if (kids)
{
for (i = 0; i < fz_array_len(kids); i++)
pdf_load_name_tree_imp(dict, xref, fz_array_get(kids, i));
}
 
if (names)
{
for (i = 0; i + 1 < fz_array_len(names); i += 2)
{
fz_obj *key = fz_array_get(names, i);
fz_obj *val = fz_array_get(names, i + 1);
if (fz_is_string(key))
{
key = pdf_to_utf8_name(key);
fz_dict_put(dict, key, val);
fz_drop_obj(key);
}
else if (fz_is_name(key))
{
fz_dict_put(dict, key, val);
}
}
}
}
 
fz_obj *
pdf_load_name_tree(pdf_xref *xref, char *which)
{
fz_obj *root = fz_dict_gets(xref->trailer, "Root");
fz_obj *names = fz_dict_gets(root, "Names");
fz_obj *tree = fz_dict_gets(names, which);
if (fz_is_dict(tree))
{
fz_obj *dict = fz_new_dict(100);
pdf_load_name_tree_imp(dict, xref, tree);
return dict;
}
return NULL;
}
/contrib/media/updf/pdf/pdf_outline.c
0,0 → 1,93
#include "fitz.h"
#include "mupdf.h"
 
static pdf_outline *
pdf_load_outline_imp(pdf_xref *xref, fz_obj *dict)
{
pdf_outline *node;
fz_obj *obj;
 
if (fz_is_null(dict))
return NULL;
 
node = fz_malloc(sizeof(pdf_outline));
node->title = NULL;
node->link = NULL;
node->child = NULL;
node->next = NULL;
node->count = 0;
 
obj = fz_dict_gets(dict, "Title");
if (obj)
node->title = pdf_to_utf8(obj);
 
obj = fz_dict_gets(dict, "Count");
if (obj)
node->count = fz_to_int(obj);
 
if (fz_dict_gets(dict, "Dest") || fz_dict_gets(dict, "A"))
node->link = pdf_load_link(xref, dict);
 
obj = fz_dict_gets(dict, "First");
if (obj)
node->child = pdf_load_outline_imp(xref, obj);
 
obj = fz_dict_gets(dict, "Next");
if (obj)
node->next = pdf_load_outline_imp(xref, obj);
 
return node;
}
 
pdf_outline *
pdf_load_outline(pdf_xref *xref)
{
fz_obj *root, *obj, *first;
 
root = fz_dict_gets(xref->trailer, "Root");
obj = fz_dict_gets(root, "Outlines");
first = fz_dict_gets(obj, "First");
if (first)
return pdf_load_outline_imp(xref, first);
 
return NULL;
}
 
void
pdf_free_outline(pdf_outline *outline)
{
if (outline->child)
pdf_free_outline(outline->child);
if (outline->next)
pdf_free_outline(outline->next);
if (outline->link)
pdf_free_link(outline->link);
fz_free(outline->title);
fz_free(outline);
}
 
void
pdf_debug_outline(pdf_outline *outline, int level)
{
int i;
while (outline)
{
for (i = 0; i < level; i++)
putchar(' ');
 
if (outline->title)
printf("%s ", outline->title);
else
printf("<NULL> ");
 
if (outline->link)
fz_debug_obj(outline->link->dest);
else
printf("<NULL>\n");
 
if (outline->child)
pdf_debug_outline(outline->child, level + 2);
 
outline = outline->next;
}
}
/contrib/media/updf/pdf/pdf_page.c
0,0 → 1,370
#include "fitz.h"
#include "mupdf.h"
 
struct info
{
fz_obj *resources;
fz_obj *mediabox;
fz_obj *cropbox;
fz_obj *rotate;
};
 
int
pdf_count_pages(pdf_xref *xref)
{
return xref->page_len;
}
 
int
pdf_find_page_number(pdf_xref *xref, fz_obj *page)
{
int i, num = fz_to_num(page);
for (i = 0; i < xref->page_len; i++)
if (num == fz_to_num(xref->page_refs[i]))
return i;
return -1;
}
 
static void
pdf_load_page_tree_node(pdf_xref *xref, fz_obj *node, struct info info)
{
fz_obj *dict, *kids, *count;
fz_obj *obj, *tmp;
int i, n;
 
/* prevent infinite recursion */
if (fz_dict_gets(node, ".seen"))
return;
 
kids = fz_dict_gets(node, "Kids");
count = fz_dict_gets(node, "Count");
 
if (fz_is_array(kids) && fz_is_int(count))
{
obj = fz_dict_gets(node, "Resources");
if (obj)
info.resources = obj;
obj = fz_dict_gets(node, "MediaBox");
if (obj)
info.mediabox = obj;
obj = fz_dict_gets(node, "CropBox");
if (obj)
info.cropbox = obj;
obj = fz_dict_gets(node, "Rotate");
if (obj)
info.rotate = obj;
 
tmp = fz_new_null();
fz_dict_puts(node, ".seen", tmp);
fz_drop_obj(tmp);
 
n = fz_array_len(kids);
for (i = 0; i < n; i++)
{
obj = fz_array_get(kids, i);
pdf_load_page_tree_node(xref, obj, info);
}
 
fz_dict_dels(node, ".seen");
}
else
{
dict = fz_resolve_indirect(node);
 
if (info.resources && !fz_dict_gets(dict, "Resources"))
fz_dict_puts(dict, "Resources", info.resources);
if (info.mediabox && !fz_dict_gets(dict, "MediaBox"))
fz_dict_puts(dict, "MediaBox", info.mediabox);
if (info.cropbox && !fz_dict_gets(dict, "CropBox"))
fz_dict_puts(dict, "CropBox", info.cropbox);
if (info.rotate && !fz_dict_gets(dict, "Rotate"))
fz_dict_puts(dict, "Rotate", info.rotate);
 
if (xref->page_len == xref->page_cap)
{
fz_warn("found more pages than expected");
xref->page_cap ++;
xref->page_refs = fz_realloc(xref->page_refs, xref->page_cap, sizeof(fz_obj*));
xref->page_objs = fz_realloc(xref->page_objs, xref->page_cap, sizeof(fz_obj*));
}
 
xref->page_refs[xref->page_len] = fz_keep_obj(node);
xref->page_objs[xref->page_len] = fz_keep_obj(dict);
xref->page_len ++;
}
}
 
fz_error
pdf_load_page_tree(pdf_xref *xref)
{
struct info info;
fz_obj *catalog = fz_dict_gets(xref->trailer, "Root");
fz_obj *pages = fz_dict_gets(catalog, "Pages");
fz_obj *count = fz_dict_gets(pages, "Count");
 
if (!fz_is_dict(pages))
return fz_throw("missing page tree");
if (!fz_is_int(count))
return fz_throw("missing page count");
 
xref->page_cap = fz_to_int(count);
xref->page_len = 0;
xref->page_refs = fz_calloc(xref->page_cap, sizeof(fz_obj*));
xref->page_objs = fz_calloc(xref->page_cap, sizeof(fz_obj*));
 
info.resources = NULL;
info.mediabox = NULL;
info.cropbox = NULL;
info.rotate = NULL;
 
pdf_load_page_tree_node(xref, pages, info);
 
return fz_okay;
}
 
/* We need to know whether to install a page-level transparency group */
 
static int pdf_resources_use_blending(fz_obj *rdb);
 
static int
pdf_extgstate_uses_blending(fz_obj *dict)
{
fz_obj *obj = fz_dict_gets(dict, "BM");
if (fz_is_name(obj) && strcmp(fz_to_name(obj), "Normal"))
return 1;
return 0;
}
 
static int
pdf_pattern_uses_blending(fz_obj *dict)
{
fz_obj *obj;
obj = fz_dict_gets(dict, "Resources");
if (pdf_resources_use_blending(obj))
return 1;
obj = fz_dict_gets(dict, "ExtGState");
if (pdf_extgstate_uses_blending(obj))
return 1;
return 0;
}
 
static int
pdf_xobject_uses_blending(fz_obj *dict)
{
fz_obj *obj = fz_dict_gets(dict, "Resources");
if (pdf_resources_use_blending(obj))
return 1;
return 0;
}
 
static int
pdf_resources_use_blending(fz_obj *rdb)
{
fz_obj *dict;
fz_obj *tmp;
int i;
 
if (!rdb)
return 0;
 
/* stop on cyclic resource dependencies */
if (fz_dict_gets(rdb, ".useBM"))
return fz_to_bool(fz_dict_gets(rdb, ".useBM"));
 
tmp = fz_new_bool(0);
fz_dict_puts(rdb, ".useBM", tmp);
fz_drop_obj(tmp);
 
dict = fz_dict_gets(rdb, "ExtGState");
for (i = 0; i < fz_dict_len(dict); i++)
if (pdf_extgstate_uses_blending(fz_dict_get_val(dict, i)))
goto found;
 
dict = fz_dict_gets(rdb, "Pattern");
for (i = 0; i < fz_dict_len(dict); i++)
if (pdf_pattern_uses_blending(fz_dict_get_val(dict, i)))
goto found;
 
dict = fz_dict_gets(rdb, "XObject");
for (i = 0; i < fz_dict_len(dict); i++)
if (pdf_xobject_uses_blending(fz_dict_get_val(dict, i)))
goto found;
 
return 0;
 
found:
tmp = fz_new_bool(1);
fz_dict_puts(rdb, ".useBM", tmp);
fz_drop_obj(tmp);
return 1;
}
 
/* we need to combine all sub-streams into one for the content stream interpreter */
 
static fz_error
pdf_load_page_contents_array(fz_buffer **bigbufp, pdf_xref *xref, fz_obj *list)
{
fz_error error;
fz_buffer *big;
fz_buffer *one;
int i, n;
 
big = fz_new_buffer(32 * 1024);
 
n = fz_array_len(list);
for (i = 0; i < n; i++)
{
fz_obj *stm = fz_array_get(list, i);
error = pdf_load_stream(&one, xref, fz_to_num(stm), fz_to_gen(stm));
if (error)
{
fz_catch(error, "cannot load content stream part %d/%d", i + 1, n);
continue;
}
 
if (big->len + one->len + 1 > big->cap)
fz_resize_buffer(big, big->len + one->len + 1);
memcpy(big->data + big->len, one->data, one->len);
big->data[big->len + one->len] = ' ';
big->len += one->len + 1;
 
fz_drop_buffer(one);
}
 
if (n > 0 && big->len == 0)
{
fz_drop_buffer(big);
return fz_throw("cannot load content stream");
}
 
*bigbufp = big;
return fz_okay;
}
 
static fz_error
pdf_load_page_contents(fz_buffer **bufp, pdf_xref *xref, fz_obj *obj)
{
fz_error error;
 
if (fz_is_array(obj))
{
error = pdf_load_page_contents_array(bufp, xref, obj);
if (error)
return fz_rethrow(error, "cannot load content stream array");
}
else if (pdf_is_stream(xref, fz_to_num(obj), fz_to_gen(obj)))
{
error = pdf_load_stream(bufp, xref, fz_to_num(obj), fz_to_gen(obj));
if (error)
return fz_rethrow(error, "cannot load content stream (%d 0 R)", fz_to_num(obj));
}
else
{
fz_warn("page contents missing, leaving page blank");
*bufp = fz_new_buffer(0);
}
 
return fz_okay;
}
 
fz_error
pdf_load_page(pdf_page **pagep, pdf_xref *xref, int number)
{
fz_error error;
pdf_page *page;
pdf_annot *annot;
fz_obj *pageobj, *pageref;
fz_obj *obj;
fz_bbox bbox;
 
if (number < 0 || number >= xref->page_len)
return fz_throw("cannot find page %d", number + 1);
 
/* Ensure that we have a store for resource objects */
if (!xref->store)
xref->store = pdf_new_store();
 
pageobj = xref->page_objs[number];
pageref = xref->page_refs[number];
 
page = fz_malloc(sizeof(pdf_page));
page->resources = NULL;
page->contents = NULL;
page->transparency = 0;
page->links = NULL;
page->annots = NULL;
 
obj = fz_dict_gets(pageobj, "MediaBox");
bbox = fz_round_rect(pdf_to_rect(obj));
if (fz_is_empty_rect(pdf_to_rect(obj)))
{
fz_warn("cannot find page size for page %d", number + 1);
bbox.x0 = 0;
bbox.y0 = 0;
bbox.x1 = 612;
bbox.y1 = 792;
}
 
obj = fz_dict_gets(pageobj, "CropBox");
if (fz_is_array(obj))
{
fz_bbox cropbox = fz_round_rect(pdf_to_rect(obj));
bbox = fz_intersect_bbox(bbox, cropbox);
}
 
page->mediabox.x0 = MIN(bbox.x0, bbox.x1);
page->mediabox.y0 = MIN(bbox.y0, bbox.y1);
page->mediabox.x1 = MAX(bbox.x0, bbox.x1);
page->mediabox.y1 = MAX(bbox.y0, bbox.y1);
 
if (page->mediabox.x1 - page->mediabox.x0 < 1 || page->mediabox.y1 - page->mediabox.y0 < 1)
{
fz_warn("invalid page size in page %d", number + 1);
page->mediabox = fz_unit_rect;
}
 
page->rotate = fz_to_int(fz_dict_gets(pageobj, "Rotate"));
 
obj = fz_dict_gets(pageobj, "Annots");
if (obj)
{
pdf_load_links(&page->links, xref, obj);
pdf_load_annots(&page->annots, xref, obj);
}
 
page->resources = fz_dict_gets(pageobj, "Resources");
if (page->resources)
fz_keep_obj(page->resources);
 
obj = fz_dict_gets(pageobj, "Contents");
error = pdf_load_page_contents(&page->contents, xref, obj);
if (error)
{
pdf_free_page(page);
return fz_rethrow(error, "cannot load page %d contents (%d 0 R)", number + 1, fz_to_num(pageref));
}
 
if (pdf_resources_use_blending(page->resources))
page->transparency = 1;
 
for (annot = page->annots; annot && !page->transparency; annot = annot->next)
if (pdf_resources_use_blending(annot->ap->resources))
page->transparency = 1;
 
*pagep = page;
return fz_okay;
}
 
void
pdf_free_page(pdf_page *page)
{
if (page->resources)
fz_drop_obj(page->resources);
if (page->contents)
fz_drop_buffer(page->contents);
if (page->links)
pdf_free_link(page->links);
if (page->annots)
pdf_free_annot(page->annots);
fz_free(page);
}
/contrib/media/updf/pdf/pdf_parse.c
0,0 → 1,581
#include "fitz.h"
#include "mupdf.h"
 
fz_rect
pdf_to_rect(fz_obj *array)
{
fz_rect r;
float a = fz_to_real(fz_array_get(array, 0));
float b = fz_to_real(fz_array_get(array, 1));
float c = fz_to_real(fz_array_get(array, 2));
float d = fz_to_real(fz_array_get(array, 3));
r.x0 = MIN(a, c);
r.y0 = MIN(b, d);
r.x1 = MAX(a, c);
r.y1 = MAX(b, d);
return r;
}
 
fz_matrix
pdf_to_matrix(fz_obj *array)
{
fz_matrix m;
m.a = fz_to_real(fz_array_get(array, 0));
m.b = fz_to_real(fz_array_get(array, 1));
m.c = fz_to_real(fz_array_get(array, 2));
m.d = fz_to_real(fz_array_get(array, 3));
m.e = fz_to_real(fz_array_get(array, 4));
m.f = fz_to_real(fz_array_get(array, 5));
return m;
}
 
/* Convert Unicode/PdfDocEncoding string into utf-8 */
char *
pdf_to_utf8(fz_obj *src)
{
unsigned char *srcptr = (unsigned char *) fz_to_str_buf(src);
char *dstptr, *dst;
int srclen = fz_to_str_len(src);
int dstlen = 0;
int ucs;
int i;
 
if (srclen > 2 && srcptr[0] == 254 && srcptr[1] == 255)
{
for (i = 2; i < srclen; i += 2)
{
ucs = (srcptr[i] << 8) | srcptr[i+1];
dstlen += runelen(ucs);
}
 
dstptr = dst = fz_malloc(dstlen + 1);
 
for (i = 2; i < srclen; i += 2)
{
ucs = (srcptr[i] << 8) | srcptr[i+1];
dstptr += runetochar(dstptr, &ucs);
}
}
 
else
{
for (i = 0; i < srclen; i++)
dstlen += runelen(pdf_doc_encoding[srcptr[i]]);
 
dstptr = dst = fz_malloc(dstlen + 1);
 
for (i = 0; i < srclen; i++)
{
ucs = pdf_doc_encoding[srcptr[i]];
dstptr += runetochar(dstptr, &ucs);
}
}
 
*dstptr = '\0';
return dst;
}
 
/* Convert Unicode/PdfDocEncoding string into ucs-2 */
unsigned short *
pdf_to_ucs2(fz_obj *src)
{
unsigned char *srcptr = (unsigned char *) fz_to_str_buf(src);
unsigned short *dstptr, *dst;
int srclen = fz_to_str_len(src);
int i;
 
if (srclen > 2 && srcptr[0] == 254 && srcptr[1] == 255)
{
dstptr = dst = fz_calloc((srclen - 2) / 2 + 1, sizeof(short));
for (i = 2; i < srclen; i += 2)
*dstptr++ = (srcptr[i] << 8) | srcptr[i+1];
}
 
else
{
dstptr = dst = fz_calloc(srclen + 1, sizeof(short));
for (i = 0; i < srclen; i++)
*dstptr++ = pdf_doc_encoding[srcptr[i]];
}
 
*dstptr = '\0';
return dst;
}
 
/* Convert UCS-2 string into PdfDocEncoding for authentication */
char *
pdf_from_ucs2(unsigned short *src)
{
int i, j, len;
char *docstr;
 
len = 0;
while (src[len])
len++;
 
docstr = fz_malloc(len + 1);
 
for (i = 0; i < len; i++)
{
/* shortcut: check if the character has the same code point in both encodings */
if (0 < src[i] && src[i] < 256 && pdf_doc_encoding[src[i]] == src[i]) {
docstr[i] = src[i];
continue;
}
 
/* search through pdf_docencoding for the character's code point */
for (j = 0; j < 256; j++)
if (pdf_doc_encoding[j] == src[i])
break;
docstr[i] = j;
 
/* fail, if a character can't be encoded */
if (!docstr[i])
{
fz_free(docstr);
return NULL;
}
}
docstr[len] = '\0';
 
return docstr;
}
 
fz_obj *
pdf_to_utf8_name(fz_obj *src)
{
char *buf = pdf_to_utf8(src);
fz_obj *dst = fz_new_name(buf);
fz_free(buf);
return dst;
}
 
fz_error
pdf_parse_array(fz_obj **op, pdf_xref *xref, fz_stream *file, char *buf, int cap)
{
fz_error error = fz_okay;
fz_obj *ary = NULL;
fz_obj *obj = NULL;
int a = 0, b = 0, n = 0;
int tok;
int len;
 
ary = fz_new_array(4);
 
while (1)
{
error = pdf_lex(&tok, file, buf, cap, &len);
if (error)
{
fz_drop_obj(ary);
return fz_rethrow(error, "cannot parse array");
}
 
if (tok != PDF_TOK_INT && tok != PDF_TOK_R)
{
if (n > 0)
{
obj = fz_new_int(a);
fz_array_push(ary, obj);
fz_drop_obj(obj);
}
if (n > 1)
{
obj = fz_new_int(b);
fz_array_push(ary, obj);
fz_drop_obj(obj);
}
n = 0;
}
 
if (tok == PDF_TOK_INT && n == 2)
{
obj = fz_new_int(a);
fz_array_push(ary, obj);
fz_drop_obj(obj);
a = b;
n --;
}
 
switch (tok)
{
case PDF_TOK_CLOSE_ARRAY:
*op = ary;
return fz_okay;
 
case PDF_TOK_INT:
if (n == 0)
a = atoi(buf);
if (n == 1)
b = atoi(buf);
n ++;
break;
 
case PDF_TOK_R:
if (n != 2)
{
fz_drop_obj(ary);
return fz_throw("cannot parse indirect reference in array");
}
obj = fz_new_indirect(a, b, xref);
fz_array_push(ary, obj);
fz_drop_obj(obj);
n = 0;
break;
 
case PDF_TOK_OPEN_ARRAY:
error = pdf_parse_array(&obj, xref, file, buf, cap);
if (error)
{
fz_drop_obj(ary);
return fz_rethrow(error, "cannot parse array");
}
fz_array_push(ary, obj);
fz_drop_obj(obj);
break;
 
case PDF_TOK_OPEN_DICT:
error = pdf_parse_dict(&obj, xref, file, buf, cap);
if (error)
{
fz_drop_obj(ary);
return fz_rethrow(error, "cannot parse array");
}
fz_array_push(ary, obj);
fz_drop_obj(obj);
break;
 
case PDF_TOK_NAME:
obj = fz_new_name(buf);
fz_array_push(ary, obj);
fz_drop_obj(obj);
break;
case PDF_TOK_REAL:
obj = fz_new_real(fz_atof(buf));
fz_array_push(ary, obj);
fz_drop_obj(obj);
break;
case PDF_TOK_STRING:
obj = fz_new_string(buf, len);
fz_array_push(ary, obj);
fz_drop_obj(obj);
break;
case PDF_TOK_TRUE:
obj = fz_new_bool(1);
fz_array_push(ary, obj);
fz_drop_obj(obj);
break;
case PDF_TOK_FALSE:
obj = fz_new_bool(0);
fz_array_push(ary, obj);
fz_drop_obj(obj);
break;
case PDF_TOK_NULL:
obj = fz_new_null();
fz_array_push(ary, obj);
fz_drop_obj(obj);
break;
 
default:
fz_drop_obj(ary);
return fz_throw("cannot parse token in array");
}
}
}
 
fz_error
pdf_parse_dict(fz_obj **op, pdf_xref *xref, fz_stream *file, char *buf, int cap)
{
fz_error error = fz_okay;
fz_obj *dict = NULL;
fz_obj *key = NULL;
fz_obj *val = NULL;
int tok;
int len;
int a, b;
 
dict = fz_new_dict(8);
 
while (1)
{
error = pdf_lex(&tok, file, buf, cap, &len);
if (error)
{
fz_drop_obj(dict);
return fz_rethrow(error, "cannot parse dict");
}
 
skip:
if (tok == PDF_TOK_CLOSE_DICT)
{
*op = dict;
return fz_okay;
}
 
/* for BI .. ID .. EI in content streams */
if (tok == PDF_TOK_KEYWORD && !strcmp(buf, "ID"))
{
*op = dict;
return fz_okay;
}
 
if (tok != PDF_TOK_NAME)
{
fz_drop_obj(dict);
return fz_throw("invalid key in dict");
}
 
key = fz_new_name(buf);
 
error = pdf_lex(&tok, file, buf, cap, &len);
if (error)
{
fz_drop_obj(key);
fz_drop_obj(dict);
return fz_rethrow(error, "cannot parse dict");
}
 
switch (tok)
{
case PDF_TOK_OPEN_ARRAY:
error = pdf_parse_array(&val, xref, file, buf, cap);
if (error)
{
fz_drop_obj(key);
fz_drop_obj(dict);
return fz_rethrow(error, "cannot parse dict");
}
break;
 
case PDF_TOK_OPEN_DICT:
error = pdf_parse_dict(&val, xref, file, buf, cap);
if (error)
{
fz_drop_obj(key);
fz_drop_obj(dict);
return fz_rethrow(error, "cannot parse dict");
}
break;
 
case PDF_TOK_NAME: val = fz_new_name(buf); break;
case PDF_TOK_REAL: val = fz_new_real(fz_atof(buf)); break;
case PDF_TOK_STRING: val = fz_new_string(buf, len); break;
case PDF_TOK_TRUE: val = fz_new_bool(1); break;
case PDF_TOK_FALSE: val = fz_new_bool(0); break;
case PDF_TOK_NULL: val = fz_new_null(); break;
 
case PDF_TOK_INT:
/* 64-bit to allow for numbers > INT_MAX and overflow */
a = (int) strtoll(buf, 0, 10);
error = pdf_lex(&tok, file, buf, cap, &len);
if (error)
{
fz_drop_obj(key);
fz_drop_obj(dict);
return fz_rethrow(error, "cannot parse dict");
}
if (tok == PDF_TOK_CLOSE_DICT || tok == PDF_TOK_NAME ||
(tok == PDF_TOK_KEYWORD && !strcmp(buf, "ID")))
{
val = fz_new_int(a);
fz_dict_put(dict, key, val);
fz_drop_obj(val);
fz_drop_obj(key);
goto skip;
}
if (tok == PDF_TOK_INT)
{
b = atoi(buf);
error = pdf_lex(&tok, file, buf, cap, &len);
if (error)
{
fz_drop_obj(key);
fz_drop_obj(dict);
return fz_rethrow(error, "cannot parse dict");
}
if (tok == PDF_TOK_R)
{
val = fz_new_indirect(a, b, xref);
break;
}
}
fz_drop_obj(key);
fz_drop_obj(dict);
return fz_throw("invalid indirect reference in dict");
 
default:
fz_drop_obj(key);
fz_drop_obj(dict);
return fz_throw("unknown token in dict");
}
 
fz_dict_put(dict, key, val);
fz_drop_obj(val);
fz_drop_obj(key);
}
}
 
fz_error
pdf_parse_stm_obj(fz_obj **op, pdf_xref *xref, fz_stream *file, char *buf, int cap)
{
fz_error error;
int tok;
int len;
 
error = pdf_lex(&tok, file, buf, cap, &len);
if (error)
return fz_rethrow(error, "cannot parse token in object stream");
 
switch (tok)
{
case PDF_TOK_OPEN_ARRAY:
error = pdf_parse_array(op, xref, file, buf, cap);
if (error)
return fz_rethrow(error, "cannot parse object stream");
break;
case PDF_TOK_OPEN_DICT:
error = pdf_parse_dict(op, xref, file, buf, cap);
if (error)
return fz_rethrow(error, "cannot parse object stream");
break;
case PDF_TOK_NAME: *op = fz_new_name(buf); break;
case PDF_TOK_REAL: *op = fz_new_real(fz_atof(buf)); break;
case PDF_TOK_STRING: *op = fz_new_string(buf, len); break;
case PDF_TOK_TRUE: *op = fz_new_bool(1); break;
case PDF_TOK_FALSE: *op = fz_new_bool(0); break;
case PDF_TOK_NULL: *op = fz_new_null(); break;
case PDF_TOK_INT: *op = fz_new_int(atoi(buf)); break;
default: return fz_throw("unknown token in object stream");
}
 
return fz_okay;
}
 
fz_error
pdf_parse_ind_obj(fz_obj **op, pdf_xref *xref,
fz_stream *file, char *buf, int cap,
int *onum, int *ogen, int *ostmofs)
{
fz_error error = fz_okay;
fz_obj *obj = NULL;
int num = 0, gen = 0, stm_ofs;
int tok;
int len;
int a, b;
 
error = pdf_lex(&tok, file, buf, cap, &len);
if (error)
return fz_rethrow(error, "cannot parse indirect object (%d %d R)", num, gen);
if (tok != PDF_TOK_INT)
return fz_throw("expected object number (%d %d R)", num, gen);
num = atoi(buf);
 
error = pdf_lex(&tok, file, buf, cap, &len);
if (error)
return fz_rethrow(error, "cannot parse indirect object (%d %d R)", num, gen);
if (tok != PDF_TOK_INT)
return fz_throw("expected generation number (%d %d R)", num, gen);
gen = atoi(buf);
 
error = pdf_lex(&tok, file, buf, cap, &len);
if (error)
return fz_rethrow(error, "cannot parse indirect object (%d %d R)", num, gen);
if (tok != PDF_TOK_OBJ)
return fz_throw("expected 'obj' keyword (%d %d R)", num, gen);
 
error = pdf_lex(&tok, file, buf, cap, &len);
if (error)
return fz_rethrow(error, "cannot parse indirect object (%d %d R)", num, gen);
 
switch (tok)
{
case PDF_TOK_OPEN_ARRAY:
error = pdf_parse_array(&obj, xref, file, buf, cap);
if (error)
return fz_rethrow(error, "cannot parse indirect object (%d %d R)", num, gen);
break;
 
case PDF_TOK_OPEN_DICT:
error = pdf_parse_dict(&obj, xref, file, buf, cap);
if (error)
return fz_rethrow(error, "cannot parse indirect object (%d %d R)", num, gen);
break;
 
case PDF_TOK_NAME: obj = fz_new_name(buf); break;
case PDF_TOK_REAL: obj = fz_new_real(fz_atof(buf)); break;
case PDF_TOK_STRING: obj = fz_new_string(buf, len); break;
case PDF_TOK_TRUE: obj = fz_new_bool(1); break;
case PDF_TOK_FALSE: obj = fz_new_bool(0); break;
case PDF_TOK_NULL: obj = fz_new_null(); break;
 
case PDF_TOK_INT:
a = atoi(buf);
error = pdf_lex(&tok, file, buf, cap, &len);
if (error)
return fz_rethrow(error, "cannot parse indirect object (%d %d R)", num, gen);
if (tok == PDF_TOK_STREAM || tok == PDF_TOK_ENDOBJ)
{
obj = fz_new_int(a);
goto skip;
}
if (tok == PDF_TOK_INT)
{
b = atoi(buf);
error = pdf_lex(&tok, file, buf, cap, &len);
if (error)
return fz_rethrow(error, "cannot parse indirect object (%d %d R)", num, gen);
if (tok == PDF_TOK_R)
{
obj = fz_new_indirect(a, b, xref);
break;
}
}
return fz_throw("expected 'R' keyword (%d %d R)", num, gen);
 
case PDF_TOK_ENDOBJ:
obj = fz_new_null();
goto skip;
 
default:
return fz_throw("syntax error in object (%d %d R)", num, gen);
}
 
error = pdf_lex(&tok, file, buf, cap, &len);
if (error)
{
fz_drop_obj(obj);
return fz_rethrow(error, "cannot parse indirect object (%d %d R)", num, gen);
}
 
skip:
if (tok == PDF_TOK_STREAM)
{
int c = fz_read_byte(file);
while (c == ' ')
c = fz_read_byte(file);
if (c == '\r')
{
c = fz_peek_byte(file);
if (c != '\n')
fz_warn("line feed missing after stream begin marker (%d %d R)", num, gen);
else
fz_read_byte(file);
}
stm_ofs = fz_tell(file);
}
else if (tok == PDF_TOK_ENDOBJ)
{
stm_ofs = 0;
}
else
{
fz_warn("expected 'endobj' or 'stream' keyword (%d %d R)", num, gen);
stm_ofs = 0;
}
 
if (onum) *onum = num;
if (ogen) *ogen = gen;
if (ostmofs) *ostmofs = stm_ofs;
*op = obj;
return fz_okay;
}
/contrib/media/updf/pdf/pdf_pattern.c
0,0 → 1,72
#include "fitz.h"
#include "mupdf.h"
 
fz_error
pdf_load_pattern(pdf_pattern **patp, pdf_xref *xref, fz_obj *dict)
{
fz_error error;
pdf_pattern *pat;
fz_obj *obj;
 
if ((*patp = pdf_find_item(xref->store, pdf_drop_pattern, dict)))
{
pdf_keep_pattern(*patp);
return fz_okay;
}
 
pat = fz_malloc(sizeof(pdf_pattern));
pat->refs = 1;
pat->resources = NULL;
pat->contents = NULL;
 
/* Store pattern now, to avoid possible recursion if objects refer back to this one */
pdf_store_item(xref->store, pdf_keep_pattern, pdf_drop_pattern, dict, pat);
 
pat->ismask = fz_to_int(fz_dict_gets(dict, "PaintType")) == 2;
pat->xstep = fz_to_real(fz_dict_gets(dict, "XStep"));
pat->ystep = fz_to_real(fz_dict_gets(dict, "YStep"));
 
obj = fz_dict_gets(dict, "BBox");
pat->bbox = pdf_to_rect(obj);
 
obj = fz_dict_gets(dict, "Matrix");
if (obj)
pat->matrix = pdf_to_matrix(obj);
else
pat->matrix = fz_identity;
 
pat->resources = fz_dict_gets(dict, "Resources");
if (pat->resources)
fz_keep_obj(pat->resources);
 
error = pdf_load_stream(&pat->contents, xref, fz_to_num(dict), fz_to_gen(dict));
if (error)
{
pdf_remove_item(xref->store, pdf_drop_pattern, dict);
pdf_drop_pattern(pat);
return fz_rethrow(error, "cannot load pattern stream (%d %d R)", fz_to_num(dict), fz_to_gen(dict));
}
 
*patp = pat;
return fz_okay;
}
 
pdf_pattern *
pdf_keep_pattern(pdf_pattern *pat)
{
pat->refs ++;
return pat;
}
 
void
pdf_drop_pattern(pdf_pattern *pat)
{
if (pat && --pat->refs == 0)
{
if (pat->resources)
fz_drop_obj(pat->resources);
if (pat->contents)
fz_drop_buffer(pat->contents);
fz_free(pat);
}
}
/contrib/media/updf/pdf/pdf_repair.c
0,0 → 1,463
#include "fitz.h"
#include "mupdf.h"
 
/* Scan file for objects and reconstruct xref table */
 
struct entry
{
int num;
int gen;
int ofs;
int stm_ofs;
int stm_len;
};
 
static fz_error
pdf_repair_obj(fz_stream *file, char *buf, int cap, int *stmofsp, int *stmlenp, fz_obj **encrypt, fz_obj **id)
{
fz_error error;
int tok;
int stm_len;
int len;
int n;
 
*stmofsp = 0;
*stmlenp = -1;
 
stm_len = 0;
 
error = pdf_lex(&tok, file, buf, cap, &len);
if (error)
return fz_rethrow(error, "cannot parse object");
if (tok == PDF_TOK_OPEN_DICT)
{
fz_obj *dict, *obj;
 
/* Send NULL xref so we don't try to resolve references */
error = pdf_parse_dict(&dict, NULL, file, buf, cap);
if (error)
return fz_rethrow(error, "cannot parse object");
 
obj = fz_dict_gets(dict, "Type");
if (fz_is_name(obj) && !strcmp(fz_to_name(obj), "XRef"))
{
obj = fz_dict_gets(dict, "Encrypt");
if (obj)
{
if (*encrypt)
fz_drop_obj(*encrypt);
*encrypt = fz_keep_obj(obj);
}
 
obj = fz_dict_gets(dict, "ID");
if (obj)
{
if (*id)
fz_drop_obj(*id);
*id = fz_keep_obj(obj);
}
}
 
obj = fz_dict_gets(dict, "Length");
if (fz_is_int(obj))
stm_len = fz_to_int(obj);
 
fz_drop_obj(dict);
}
 
while ( tok != PDF_TOK_STREAM &&
tok != PDF_TOK_ENDOBJ &&
tok != PDF_TOK_ERROR &&
tok != PDF_TOK_EOF )
{
error = pdf_lex(&tok, file, buf, cap, &len);
if (error)
return fz_rethrow(error, "cannot scan for endobj or stream token");
}
 
if (tok == PDF_TOK_STREAM)
{
int c = fz_read_byte(file);
if (c == '\r') {
c = fz_peek_byte(file);
if (c == '\n')
fz_read_byte(file);
}
 
*stmofsp = fz_tell(file);
if (*stmofsp < 0)
return fz_throw("cannot seek in file");
 
if (stm_len > 0)
{
fz_seek(file, *stmofsp + stm_len, 0);
error = pdf_lex(&tok, file, buf, cap, &len);
if (error)
fz_catch(error, "cannot find endstream token, falling back to scanning");
if (tok == PDF_TOK_ENDSTREAM)
goto atobjend;
fz_seek(file, *stmofsp, 0);
}
 
n = fz_read(file, (unsigned char *) buf, 9);
if (n < 0)
return fz_rethrow(n, "cannot read from file");
 
while (memcmp(buf, "endstream", 9) != 0)
{
c = fz_read_byte(file);
if (c == EOF)
break;
memmove(buf, buf + 1, 8);
buf[8] = c;
}
 
*stmlenp = fz_tell(file) - *stmofsp - 9;
 
atobjend:
error = pdf_lex(&tok, file, buf, cap, &len);
if (error)
return fz_rethrow(error, "cannot scan for endobj token");
if (tok != PDF_TOK_ENDOBJ)
fz_warn("object missing 'endobj' token");
}
 
return fz_okay;
}
 
static fz_error
pdf_repair_obj_stm(pdf_xref *xref, int num, int gen)
{
fz_error error;
fz_obj *obj;
fz_stream *stm;
int tok;
int i, n, count;
char buf[256];
 
error = pdf_load_object(&obj, xref, num, gen);
if (error)
return fz_rethrow(error, "cannot load object stream object (%d %d R)", num, gen);
 
count = fz_to_int(fz_dict_gets(obj, "N"));
 
fz_drop_obj(obj);
 
error = pdf_open_stream(&stm, xref, num, gen);
if (error)
return fz_rethrow(error, "cannot open object stream object (%d %d R)", num, gen);
 
for (i = 0; i < count; i++)
{
error = pdf_lex(&tok, stm, buf, sizeof buf, &n);
if (error || tok != PDF_TOK_INT)
{
fz_close(stm);
return fz_rethrow(error, "corrupt object stream (%d %d R)", num, gen);
}
 
n = atoi(buf);
if (n >= xref->len)
pdf_resize_xref(xref, n + 1);
 
xref->table[n].ofs = num;
xref->table[n].gen = i;
xref->table[n].stm_ofs = 0;
xref->table[n].obj = NULL;
xref->table[n].type = 'o';
 
error = pdf_lex(&tok, stm, buf, sizeof buf, &n);
if (error || tok != PDF_TOK_INT)
{
fz_close(stm);
return fz_rethrow(error, "corrupt object stream (%d %d R)", num, gen);
}
}
 
fz_close(stm);
return fz_okay;
}
 
fz_error
pdf_repair_xref(pdf_xref *xref, char *buf, int bufsize)
{
fz_error error;
fz_obj *dict, *obj;
fz_obj *length;
 
fz_obj *encrypt = NULL;
fz_obj *id = NULL;
fz_obj *root = NULL;
fz_obj *info = NULL;
 
struct entry *list = NULL;
int listlen;
int listcap;
int maxnum = 0;
 
int num = 0;
int gen = 0;
int tmpofs, numofs = 0, genofs = 0;
int stm_len, stm_ofs = 0;
int tok;
int next;
int i, n, c;
 
fz_seek(xref->file, 0, 0);
 
listlen = 0;
listcap = 1024;
list = fz_calloc(listcap, sizeof(struct entry));
 
/* look for '%PDF' version marker within first kilobyte of file */
n = fz_read(xref->file, (unsigned char *)buf, MAX(bufsize, 1024));
if (n < 0)
{
error = fz_rethrow(n, "cannot read from file");
goto cleanup;
}
 
fz_seek(xref->file, 0, 0);
for (i = 0; i < n - 4; i++)
{
if (memcmp(buf + i, "%PDF", 4) == 0)
{
fz_seek(xref->file, i + 8, 0); /* skip "%PDF-X.Y" */
break;
}
}
 
/* skip comment line after version marker since some generators
* forget to terminate the comment with a newline */
c = fz_read_byte(xref->file);
while (c >= 0 && (c == ' ' || c == '%'))
c = fz_read_byte(xref->file);
fz_unread_byte(xref->file);
 
while (1)
{
tmpofs = fz_tell(xref->file);
if (tmpofs < 0)
{
error = fz_throw("cannot tell in file");
goto cleanup;
}
 
error = pdf_lex(&tok, xref->file, buf, bufsize, &n);
if (error)
{
fz_catch(error, "ignoring the rest of the file");
break;
}
 
if (tok == PDF_TOK_INT)
{
numofs = genofs;
num = gen;
genofs = tmpofs;
gen = atoi(buf);
}
 
else if (tok == PDF_TOK_OBJ)
{
error = pdf_repair_obj(xref->file, buf, bufsize, &stm_ofs, &stm_len, &encrypt, &id);
if (error)
{
error = fz_rethrow(error, "cannot parse object (%d %d R)", num, gen);
goto cleanup;
}
 
if (listlen + 1 == listcap)
{
listcap = (listcap * 3) / 2;
list = fz_realloc(list, listcap, sizeof(struct entry));
}
 
list[listlen].num = num;
list[listlen].gen = gen;
list[listlen].ofs = numofs;
list[listlen].stm_ofs = stm_ofs;
list[listlen].stm_len = stm_len;
listlen ++;
 
if (num > maxnum)
maxnum = num;
}
 
/* trailer dictionary */
else if (tok == PDF_TOK_OPEN_DICT)
{
error = pdf_parse_dict(&dict, xref, xref->file, buf, bufsize);
if (error)
{
error = fz_rethrow(error, "cannot parse object");
goto cleanup;
}
 
obj = fz_dict_gets(dict, "Encrypt");
if (obj)
{
if (encrypt)
fz_drop_obj(encrypt);
encrypt = fz_keep_obj(obj);
}
 
obj = fz_dict_gets(dict, "ID");
if (obj)
{
if (id)
fz_drop_obj(id);
id = fz_keep_obj(obj);
}
 
obj = fz_dict_gets(dict, "Root");
if (obj)
{
if (root)
fz_drop_obj(root);
root = fz_keep_obj(obj);
}
 
obj = fz_dict_gets(dict, "Info");
if (obj)
{
if (info)
fz_drop_obj(info);
info = fz_keep_obj(obj);
}
 
fz_drop_obj(dict);
}
 
else if (tok == PDF_TOK_ERROR)
fz_read_byte(xref->file);
 
else if (tok == PDF_TOK_EOF)
break;
}
 
/* make xref reasonable */
 
pdf_resize_xref(xref, maxnum + 1);
 
for (i = 0; i < listlen; i++)
{
xref->table[list[i].num].type = 'n';
xref->table[list[i].num].ofs = list[i].ofs;
xref->table[list[i].num].gen = list[i].gen;
 
xref->table[list[i].num].stm_ofs = list[i].stm_ofs;
 
/* corrected stream length */
if (list[i].stm_len >= 0)
{
error = pdf_load_object(&dict, xref, list[i].num, list[i].gen);
if (error)
{
error = fz_rethrow(error, "cannot load stream object (%d %d R)", list[i].num, list[i].gen);
goto cleanup;
}
 
length = fz_new_int(list[i].stm_len);
fz_dict_puts(dict, "Length", length);
fz_drop_obj(length);
 
fz_drop_obj(dict);
}
 
}
 
xref->table[0].type = 'f';
xref->table[0].ofs = 0;
xref->table[0].gen = 65535;
xref->table[0].stm_ofs = 0;
xref->table[0].obj = NULL;
 
next = 0;
for (i = xref->len - 1; i >= 0; i--)
{
if (xref->table[i].type == 'f')
{
xref->table[i].ofs = next;
if (xref->table[i].gen < 65535)
xref->table[i].gen ++;
next = i;
}
}
 
/* create a repaired trailer, Root will be added later */
 
xref->trailer = fz_new_dict(5);
 
obj = fz_new_int(maxnum + 1);
fz_dict_puts(xref->trailer, "Size", obj);
fz_drop_obj(obj);
 
if (root)
{
fz_dict_puts(xref->trailer, "Root", root);
fz_drop_obj(root);
}
if (info)
{
fz_dict_puts(xref->trailer, "Info", info);
fz_drop_obj(info);
}
 
if (encrypt)
{
if (fz_is_indirect(encrypt))
{
/* create new reference with non-NULL xref pointer */
obj = fz_new_indirect(fz_to_num(encrypt), fz_to_gen(encrypt), xref);
fz_drop_obj(encrypt);
encrypt = obj;
}
fz_dict_puts(xref->trailer, "Encrypt", encrypt);
fz_drop_obj(encrypt);
}
 
if (id)
{
if (fz_is_indirect(id))
{
/* create new reference with non-NULL xref pointer */
obj = fz_new_indirect(fz_to_num(id), fz_to_gen(id), xref);
fz_drop_obj(id);
id = obj;
}
fz_dict_puts(xref->trailer, "ID", id);
fz_drop_obj(id);
}
 
fz_free(list);
return fz_okay;
 
cleanup:
if (encrypt) fz_drop_obj(encrypt);
if (id) fz_drop_obj(id);
if (root) fz_drop_obj(root);
if (info) fz_drop_obj(info);
fz_free(list);
return error; /* already rethrown */
}
 
fz_error
pdf_repair_obj_stms(pdf_xref *xref)
{
fz_obj *dict;
int i;
 
for (i = 0; i < xref->len; i++)
{
if (xref->table[i].stm_ofs)
{
pdf_load_object(&dict, xref, i, 0);
if (!strcmp(fz_to_name(fz_dict_gets(dict, "Type")), "ObjStm"))
pdf_repair_obj_stm(xref, i, 0);
fz_drop_obj(dict);
}
}
 
return fz_okay;
}
/contrib/media/updf/pdf/pdf_shade.c
0,0 → 1,1140
#include "fitz.h"
#include "mupdf.h"
 
#define HUGENUM 32000 /* how far to extend axial/radial shadings */
#define FUNSEGS 32 /* size of sampled mesh for function-based shadings */
#define RADSEGS 32 /* how many segments to generate for radial meshes */
#define SUBDIV 3 /* how many levels to subdivide patches */
 
struct vertex
{
float x, y;
float c[FZ_MAX_COLORS];
};
 
static void
pdf_grow_mesh(fz_shade *shade, int amount)
{
if (shade->mesh_len + amount < shade->mesh_cap)
return;
 
if (shade->mesh_cap == 0)
shade->mesh_cap = 1024;
 
while (shade->mesh_len + amount > shade->mesh_cap)
shade->mesh_cap = (shade->mesh_cap * 3) / 2;
 
shade->mesh = fz_realloc(shade->mesh, shade->mesh_cap, sizeof(float));
}
 
static void
pdf_add_vertex(fz_shade *shade, struct vertex *v)
{
int ncomp = shade->use_function ? 1 : shade->colorspace->n;
int i;
pdf_grow_mesh(shade, 2 + ncomp);
shade->mesh[shade->mesh_len++] = v->x;
shade->mesh[shade->mesh_len++] = v->y;
for (i = 0; i < ncomp; i++)
shade->mesh[shade->mesh_len++] = v->c[i];
}
 
static void
pdf_add_triangle(fz_shade *shade,
struct vertex *v0,
struct vertex *v1,
struct vertex *v2)
{
pdf_add_vertex(shade, v0);
pdf_add_vertex(shade, v1);
pdf_add_vertex(shade, v2);
}
 
static void
pdf_add_quad(fz_shade *shade,
struct vertex *v0,
struct vertex *v1,
struct vertex *v2,
struct vertex *v3)
{
pdf_add_triangle(shade, v0, v1, v3);
pdf_add_triangle(shade, v1, v3, v2);
}
 
/* Subdivide and tesselate tensor-patches */
 
typedef struct pdf_tensor_patch_s pdf_tensor_patch;
 
struct pdf_tensor_patch_s
{
fz_point pole[4][4];
float color[4][FZ_MAX_COLORS];
};
 
static void
triangulate_patch(pdf_tensor_patch p, fz_shade *shade)
{
struct vertex v0, v1, v2, v3;
 
v0.x = p.pole[0][0].x;
v0.y = p.pole[0][0].y;
memcpy(v0.c, p.color[0], sizeof(v0.c));
 
v1.x = p.pole[0][3].x;
v1.y = p.pole[0][3].y;
memcpy(v1.c, p.color[1], sizeof(v1.c));
 
v2.x = p.pole[3][3].x;
v2.y = p.pole[3][3].y;
memcpy(v2.c, p.color[2], sizeof(v2.c));
 
v3.x = p.pole[3][0].x;
v3.y = p.pole[3][0].y;
memcpy(v3.c, p.color[3], sizeof(v3.c));
 
pdf_add_quad(shade, &v0, &v1, &v2, &v3);
}
 
static inline void midcolor(float *c, float *c1, float *c2)
{
int i;
for (i = 0; i < FZ_MAX_COLORS; i++)
c[i] = (c1[i] + c2[i]) * 0.5f;
}
 
static void
split_curve(fz_point *pole, fz_point *q0, fz_point *q1, int polestep)
{
/*
split bezier curve given by control points pole[0]..pole[3]
using de casteljau algo at midpoint and build two new
bezier curves q0[0]..q0[3] and q1[0]..q1[3]. all indices
should be multiplies by polestep == 1 for vertical bezier
curves in patch and == 4 for horizontal bezier curves due
to C's multi-dimensional matrix memory layout.
*/
 
float x12 = (pole[1 * polestep].x + pole[2 * polestep].x) * 0.5f;
float y12 = (pole[1 * polestep].y + pole[2 * polestep].y) * 0.5f;
 
q0[1 * polestep].x = (pole[0 * polestep].x + pole[1 * polestep].x) * 0.5f;
q0[1 * polestep].y = (pole[0 * polestep].y + pole[1 * polestep].y) * 0.5f;
q1[2 * polestep].x = (pole[2 * polestep].x + pole[3 * polestep].x) * 0.5f;
q1[2 * polestep].y = (pole[2 * polestep].y + pole[3 * polestep].y) * 0.5f;
 
q0[2 * polestep].x = (q0[1 * polestep].x + x12) * 0.5f;
q0[2 * polestep].y = (q0[1 * polestep].y + y12) * 0.5f;
q1[1 * polestep].x = (x12 + q1[2 * polestep].x) * 0.5f;
q1[1 * polestep].y = (y12 + q1[2 * polestep].y) * 0.5f;
 
q0[3 * polestep].x = (q0[2 * polestep].x + q1[1 * polestep].x) * 0.5f;
q0[3 * polestep].y = (q0[2 * polestep].y + q1[1 * polestep].y) * 0.5f;
q1[0 * polestep].x = (q0[2 * polestep].x + q1[1 * polestep].x) * 0.5f;
q1[0 * polestep].y = (q0[2 * polestep].y + q1[1 * polestep].y) * 0.5f;
 
q0[0 * polestep].x = pole[0 * polestep].x;
q0[0 * polestep].y = pole[0 * polestep].y;
q1[3 * polestep].x = pole[3 * polestep].x;
q1[3 * polestep].y = pole[3 * polestep].y;
}
 
static void
split_stripe(pdf_tensor_patch *p, pdf_tensor_patch *s0, pdf_tensor_patch *s1)
{
/*
split all horizontal bezier curves in patch,
creating two new patches with half the width.
*/
split_curve(&p->pole[0][0], &s0->pole[0][0], &s1->pole[0][0], 4);
split_curve(&p->pole[0][1], &s0->pole[0][1], &s1->pole[0][1], 4);
split_curve(&p->pole[0][2], &s0->pole[0][2], &s1->pole[0][2], 4);
split_curve(&p->pole[0][3], &s0->pole[0][3], &s1->pole[0][3], 4);
 
/* interpolate the colors for the two new patches. */
memcpy(s0->color[0], p->color[0], sizeof(s0->color[0]));
memcpy(s0->color[1], p->color[1], sizeof(s0->color[1]));
midcolor(s0->color[2], p->color[1], p->color[2]);
midcolor(s0->color[3], p->color[0], p->color[3]);
 
memcpy(s1->color[0], s0->color[3], sizeof(s1->color[0]));
memcpy(s1->color[1], s0->color[2], sizeof(s1->color[1]));
memcpy(s1->color[2], p->color[2], sizeof(s1->color[2]));
memcpy(s1->color[3], p->color[3], sizeof(s1->color[3]));
}
 
static void
draw_stripe(pdf_tensor_patch *p, fz_shade *shade, int depth)
{
pdf_tensor_patch s0, s1;
 
/* split patch into two half-height patches */
split_stripe(p, &s0, &s1);
 
depth--;
if (depth == 0)
{
/* if no more subdividing, draw two new patches... */
triangulate_patch(s0, shade);
triangulate_patch(s1, shade);
}
else
{
/* ...otherwise, continue subdividing. */
draw_stripe(&s0, shade, depth);
draw_stripe(&s1, shade, depth);
}
}
 
static void
split_patch(pdf_tensor_patch *p, pdf_tensor_patch *s0, pdf_tensor_patch *s1)
{
/*
split all vertical bezier curves in patch,
creating two new patches with half the height.
*/
split_curve(p->pole[0], s0->pole[0], s1->pole[0], 1);
split_curve(p->pole[1], s0->pole[1], s1->pole[1], 1);
split_curve(p->pole[2], s0->pole[2], s1->pole[2], 1);
split_curve(p->pole[3], s0->pole[3], s1->pole[3], 1);
 
/* interpolate the colors for the two new patches. */
memcpy(s0->color[0], p->color[0], sizeof(s0->color[0]));
midcolor(s0->color[1], p->color[0], p->color[1]);
midcolor(s0->color[2], p->color[2], p->color[3]);
memcpy(s0->color[3], p->color[3], sizeof(s0->color[3]));
 
memcpy(s1->color[0], s0->color[1], sizeof(s1->color[0]));
memcpy(s1->color[1], p->color[1], sizeof(s1->color[1]));
memcpy(s1->color[2], p->color[2], sizeof(s1->color[2]));
memcpy(s1->color[3], s0->color[2], sizeof(s1->color[3]));
}
 
static void
draw_patch(fz_shade *shade, pdf_tensor_patch *p, int depth, int origdepth)
{
pdf_tensor_patch s0, s1;
 
/* split patch into two half-width patches */
split_patch(p, &s0, &s1);
 
depth--;
if (depth == 0)
{
/* if no more subdividing, draw two new patches... */
draw_stripe(&s0, shade, origdepth);
draw_stripe(&s1, shade, origdepth);
}
else
{
/* ...otherwise, continue subdividing. */
draw_patch(shade, &s0, depth, origdepth);
draw_patch(shade, &s1, depth, origdepth);
}
}
 
static fz_point
pdf_compute_tensor_interior(
fz_point a, fz_point b, fz_point c, fz_point d,
fz_point e, fz_point f, fz_point g, fz_point h)
{
fz_point pt;
 
/* see equations at page 330 in pdf 1.7 */
 
pt.x = -4 * a.x;
pt.x += 6 * (b.x + c.x);
pt.x += -2 * (d.x + e.x);
pt.x += 3 * (f.x + g.x);
pt.x += -1 * h.x;
pt.x /= 9;
 
pt.y = -4 * a.y;
pt.y += 6 * (b.y + c.y);
pt.y += -2 * (d.y + e.y);
pt.y += 3 * (f.y + g.y);
pt.y += -1 * h.y;
pt.y /= 9;
 
return pt;
}
 
static void
pdf_make_tensor_patch(pdf_tensor_patch *p, int type, fz_point *pt)
{
if (type == 6)
{
/* see control point stream order at page 325 in pdf 1.7 */
 
p->pole[0][0] = pt[0];
p->pole[0][1] = pt[1];
p->pole[0][2] = pt[2];
p->pole[0][3] = pt[3];
p->pole[1][3] = pt[4];
p->pole[2][3] = pt[5];
p->pole[3][3] = pt[6];
p->pole[3][2] = pt[7];
p->pole[3][1] = pt[8];
p->pole[3][0] = pt[9];
p->pole[2][0] = pt[10];
p->pole[1][0] = pt[11];
 
/* see equations at page 330 in pdf 1.7 */
 
p->pole[1][1] = pdf_compute_tensor_interior(
p->pole[0][0], p->pole[0][1], p->pole[1][0], p->pole[0][3],
p->pole[3][0], p->pole[3][1], p->pole[1][3], p->pole[3][3]);
 
p->pole[1][2] = pdf_compute_tensor_interior(
p->pole[0][3], p->pole[0][2], p->pole[1][3], p->pole[0][0],
p->pole[3][3], p->pole[3][2], p->pole[1][0], p->pole[3][0]);
 
p->pole[2][1] = pdf_compute_tensor_interior(
p->pole[3][0], p->pole[3][1], p->pole[2][0], p->pole[3][3],
p->pole[0][0], p->pole[0][1], p->pole[2][3], p->pole[0][3]);
 
p->pole[2][2] = pdf_compute_tensor_interior(
p->pole[3][3], p->pole[3][2], p->pole[2][3], p->pole[3][0],
p->pole[0][3], p->pole[0][2], p->pole[2][0], p->pole[0][0]);
}
else if (type == 7)
{
/* see control point stream order at page 330 in pdf 1.7 */
 
p->pole[0][0] = pt[0];
p->pole[0][1] = pt[1];
p->pole[0][2] = pt[2];
p->pole[0][3] = pt[3];
p->pole[1][3] = pt[4];
p->pole[2][3] = pt[5];
p->pole[3][3] = pt[6];
p->pole[3][2] = pt[7];
p->pole[3][1] = pt[8];
p->pole[3][0] = pt[9];
p->pole[2][0] = pt[10];
p->pole[1][0] = pt[11];
p->pole[1][1] = pt[12];
p->pole[1][2] = pt[13];
p->pole[2][2] = pt[14];
p->pole[2][1] = pt[15];
}
}
 
/* Sample various functions into lookup tables */
 
static void
pdf_sample_composite_shade_function(fz_shade *shade, pdf_function *func, float t0, float t1)
{
int i;
float t;
 
for (i = 0; i < 256; i++)
{
t = t0 + (i / 255.0f) * (t1 - t0);
pdf_eval_function(func, &t, 1, shade->function[i], shade->colorspace->n);
shade->function[i][shade->colorspace->n] = 1;
}
}
 
static void
pdf_sample_component_shade_function(fz_shade *shade, int funcs, pdf_function **func, float t0, float t1)
{
int i, k;
float t;
 
for (i = 0; i < 256; i++)
{
t = t0 + (i / 255.0f) * (t1 - t0);
for (k = 0; k < funcs; k++)
pdf_eval_function(func[k], &t, 1, &shade->function[i][k], 1);
shade->function[i][k] = 1;
}
}
 
static void
pdf_sample_shade_function(fz_shade *shade, int funcs, pdf_function **func, float t0, float t1)
{
shade->use_function = 1;
if (funcs == 1)
pdf_sample_composite_shade_function(shade, func[0], t0, t1);
else
pdf_sample_component_shade_function(shade, funcs, func, t0, t1);
}
 
/* Type 1-3 -- Function-based, axial and radial shadings */
 
static void
pdf_load_function_based_shading(fz_shade *shade, pdf_xref *xref, fz_obj *dict, pdf_function *func)
{
fz_obj *obj;
float x0, y0, x1, y1;
fz_matrix matrix;
struct vertex v[4];
int xx, yy;
float x, y;
float xn, yn;
int i;
 
x0 = y0 = 0;
x1 = y1 = 1;
obj = fz_dict_gets(dict, "Domain");
if (fz_array_len(obj) == 4)
{
x0 = fz_to_real(fz_array_get(obj, 0));
x1 = fz_to_real(fz_array_get(obj, 1));
y0 = fz_to_real(fz_array_get(obj, 2));
y1 = fz_to_real(fz_array_get(obj, 3));
}
 
matrix = fz_identity;
obj = fz_dict_gets(dict, "Matrix");
if (fz_array_len(obj) == 6)
matrix = pdf_to_matrix(obj);
 
for (yy = 0; yy < FUNSEGS; yy++)
{
y = y0 + (y1 - y0) * yy / FUNSEGS;
yn = y0 + (y1 - y0) * (yy + 1) / FUNSEGS;
 
for (xx = 0; xx < FUNSEGS; xx++)
{
x = x0 + (x1 - x0) * xx / FUNSEGS;
xn = x0 + (x1 - x0) * (xx + 1) / FUNSEGS;
 
v[0].x = x; v[0].y = y;
v[1].x = xn; v[1].y = y;
v[2].x = xn; v[2].y = yn;
v[3].x = x; v[3].y = yn;
 
for (i = 0; i < 4; i++)
{
fz_point pt;
float fv[2];
 
fv[0] = v[i].x;
fv[1] = v[i].y;
pdf_eval_function(func, fv, 2, v[i].c, shade->colorspace->n);
 
pt.x = v[i].x;
pt.y = v[i].y;
pt = fz_transform_point(matrix, pt);
v[i].x = pt.x;
v[i].y = pt.y;
}
 
pdf_add_quad(shade, &v[0], &v[1], &v[2], &v[3]);
}
}
}
 
static void
pdf_load_axial_shading(fz_shade *shade, pdf_xref *xref, fz_obj *dict, int funcs, pdf_function **func)
{
fz_obj *obj;
float d0, d1;
int e0, e1;
float x0, y0, x1, y1;
struct vertex p1, p2;
 
obj = fz_dict_gets(dict, "Coords");
x0 = fz_to_real(fz_array_get(obj, 0));
y0 = fz_to_real(fz_array_get(obj, 1));
x1 = fz_to_real(fz_array_get(obj, 2));
y1 = fz_to_real(fz_array_get(obj, 3));
 
d0 = 0;
d1 = 1;
obj = fz_dict_gets(dict, "Domain");
if (fz_array_len(obj) == 2)
{
d0 = fz_to_real(fz_array_get(obj, 0));
d1 = fz_to_real(fz_array_get(obj, 1));
}
 
e0 = e1 = 0;
obj = fz_dict_gets(dict, "Extend");
if (fz_array_len(obj) == 2)
{
e0 = fz_to_bool(fz_array_get(obj, 0));
e1 = fz_to_bool(fz_array_get(obj, 1));
}
 
pdf_sample_shade_function(shade, funcs, func, d0, d1);
 
shade->type = FZ_LINEAR;
 
shade->extend[0] = e0;
shade->extend[1] = e1;
 
p1.x = x0;
p1.y = y0;
p1.c[0] = 0;
pdf_add_vertex(shade, &p1);
 
p2.x = x1;
p2.y = y1;
p2.c[0] = 0;
pdf_add_vertex(shade, &p2);
}
 
static void
pdf_load_radial_shading(fz_shade *shade, pdf_xref *xref, fz_obj *dict, int funcs, pdf_function **func)
{
fz_obj *obj;
float d0, d1;
int e0, e1;
float x0, y0, r0, x1, y1, r1;
struct vertex p1, p2;
 
obj = fz_dict_gets(dict, "Coords");
x0 = fz_to_real(fz_array_get(obj, 0));
y0 = fz_to_real(fz_array_get(obj, 1));
r0 = fz_to_real(fz_array_get(obj, 2));
x1 = fz_to_real(fz_array_get(obj, 3));
y1 = fz_to_real(fz_array_get(obj, 4));
r1 = fz_to_real(fz_array_get(obj, 5));
 
d0 = 0;
d1 = 1;
obj = fz_dict_gets(dict, "Domain");
if (fz_array_len(obj) == 2)
{
d0 = fz_to_real(fz_array_get(obj, 0));
d1 = fz_to_real(fz_array_get(obj, 1));
}
 
e0 = e1 = 0;
obj = fz_dict_gets(dict, "Extend");
if (fz_array_len(obj) == 2)
{
e0 = fz_to_bool(fz_array_get(obj, 0));
e1 = fz_to_bool(fz_array_get(obj, 1));
}
 
pdf_sample_shade_function(shade, funcs, func, d0, d1);
 
shade->type = FZ_RADIAL;
 
shade->extend[0] = e0;
shade->extend[1] = e1;
 
p1.x = x0;
p1.y = y0;
p1.c[0] = r0;
pdf_add_vertex(shade, &p1);
 
p2.x = x1;
p2.y = y1;
p2.c[0] = r1;
pdf_add_vertex(shade, &p2);
}
 
/* Type 4-7 -- Triangle and patch mesh shadings */
 
static inline float read_sample(fz_stream *stream, int bits, float min, float max)
{
/* we use pow(2,x) because (1<<x) would overflow the math on 32-bit samples */
float bitscale = 1 / (powf(2, bits) - 1);
return min + fz_read_bits(stream, bits) * (max - min) * bitscale;
}
 
struct mesh_params
{
int vprow;
int bpflag;
int bpcoord;
int bpcomp;
float x0, x1;
float y0, y1;
float c0[FZ_MAX_COLORS];
float c1[FZ_MAX_COLORS];
};
 
static void
pdf_load_mesh_params(pdf_xref *xref, fz_obj *dict, struct mesh_params *p)
{
fz_obj *obj;
int i, n;
 
p->x0 = p->y0 = 0;
p->x1 = p->y1 = 1;
for (i = 0; i < FZ_MAX_COLORS; i++)
{
p->c0[i] = 0;
p->c1[i] = 1;
}
 
p->vprow = fz_to_int(fz_dict_gets(dict, "VerticesPerRow"));
p->bpflag = fz_to_int(fz_dict_gets(dict, "BitsPerFlag"));
p->bpcoord = fz_to_int(fz_dict_gets(dict, "BitsPerCoordinate"));
p->bpcomp = fz_to_int(fz_dict_gets(dict, "BitsPerComponent"));
 
obj = fz_dict_gets(dict, "Decode");
if (fz_array_len(obj) >= 6)
{
n = (fz_array_len(obj) - 4) / 2;
p->x0 = fz_to_real(fz_array_get(obj, 0));
p->x1 = fz_to_real(fz_array_get(obj, 1));
p->y0 = fz_to_real(fz_array_get(obj, 2));
p->y1 = fz_to_real(fz_array_get(obj, 3));
for (i = 0; i < n; i++)
{
p->c0[i] = fz_to_real(fz_array_get(obj, 4 + i * 2));
p->c1[i] = fz_to_real(fz_array_get(obj, 5 + i * 2));
}
}
 
if (p->vprow < 2)
p->vprow = 2;
 
if (p->bpflag != 2 && p->bpflag != 4 && p->bpflag != 8)
p->bpflag = 8;
 
if (p->bpcoord != 1 && p->bpcoord != 2 && p->bpcoord != 4 &&
p->bpcoord != 8 && p->bpcoord != 12 && p->bpcoord != 16 &&
p->bpcoord != 24 && p->bpcoord != 32)
p->bpcoord = 8;
 
if (p->bpcomp != 1 && p->bpcomp != 2 && p->bpcomp != 4 &&
p->bpcomp != 8 && p->bpcomp != 12 && p->bpcomp != 16)
p->bpcomp = 8;
}
 
static void
pdf_load_type4_shade(fz_shade *shade, pdf_xref *xref, fz_obj *dict,
int funcs, pdf_function **func, fz_stream *stream)
{
struct mesh_params p;
struct vertex va, vb, vc, vd;
int ncomp;
int flag;
int i;
 
pdf_load_mesh_params(xref, dict, &p);
 
if (funcs > 0)
{
ncomp = 1;
pdf_sample_shade_function(shade, funcs, func, p.c0[0], p.c1[0]);
}
else
ncomp = shade->colorspace->n;
 
while (!fz_is_eof_bits(stream))
{
flag = fz_read_bits(stream, p.bpflag);
vd.x = read_sample(stream, p.bpcoord, p.x0, p.x1);
vd.y = read_sample(stream, p.bpcoord, p.y0, p.y1);
for (i = 0; i < ncomp; i++)
vd.c[i] = read_sample(stream, p.bpcomp, p.c0[i], p.c1[i]);
 
switch (flag)
{
case 0: /* start new triangle */
va = vd;
 
fz_read_bits(stream, p.bpflag);
vb.x = read_sample(stream, p.bpcoord, p.x0, p.x1);
vb.y = read_sample(stream, p.bpcoord, p.y0, p.y1);
for (i = 0; i < ncomp; i++)
vb.c[i] = read_sample(stream, p.bpcomp, p.c0[i], p.c1[i]);
 
fz_read_bits(stream, p.bpflag);
vc.x = read_sample(stream, p.bpcoord, p.x0, p.x1);
vc.y = read_sample(stream, p.bpcoord, p.y0, p.y1);
for (i = 0; i < ncomp; i++)
vc.c[i] = read_sample(stream, p.bpcomp, p.c0[i], p.c1[i]);
 
pdf_add_triangle(shade, &va, &vb, &vc);
break;
 
case 1: /* Vb, Vc, Vd */
va = vb;
vb = vc;
vc = vd;
pdf_add_triangle(shade, &va, &vb, &vc);
break;
 
case 2: /* Va, Vc, Vd */
vb = vc;
vc = vd;
pdf_add_triangle(shade, &va, &vb, &vc);
break;
}
}
}
 
static void
pdf_load_type5_shade(fz_shade *shade, pdf_xref *xref, fz_obj *dict,
int funcs, pdf_function **func, fz_stream *stream)
{
struct mesh_params p;
struct vertex *buf, *ref;
int first;
int ncomp;
int i, k;
 
pdf_load_mesh_params(xref, dict, &p);
 
if (funcs > 0)
{
ncomp = 1;
pdf_sample_shade_function(shade, funcs, func, p.c0[0], p.c1[0]);
}
else
ncomp = shade->colorspace->n;
 
ref = fz_calloc(p.vprow, sizeof(struct vertex));
buf = fz_calloc(p.vprow, sizeof(struct vertex));
first = 1;
 
while (!fz_is_eof_bits(stream))
{
for (i = 0; i < p.vprow; i++)
{
buf[i].x = read_sample(stream, p.bpcoord, p.x0, p.x1);
buf[i].y = read_sample(stream, p.bpcoord, p.y0, p.y1);
for (k = 0; k < ncomp; k++)
buf[i].c[k] = read_sample(stream, p.bpcomp, p.c0[k], p.c1[k]);
}
 
if (!first)
for (i = 0; i < p.vprow - 1; i++)
pdf_add_quad(shade,
&ref[i], &ref[i+1], &buf[i+1], &buf[i]);
 
memcpy(ref, buf, p.vprow * sizeof(struct vertex));
first = 0;
}
 
free(ref);
free(buf);
}
 
/* Type 6 & 7 -- Patch mesh shadings */
 
static void
pdf_load_type6_shade(fz_shade *shade, pdf_xref *xref, fz_obj *dict,
int funcs, pdf_function **func, fz_stream *stream)
{
struct mesh_params p;
int haspatch, hasprevpatch;
float prevc[4][FZ_MAX_COLORS];
fz_point prevp[12];
int ncomp;
int i, k;
 
pdf_load_mesh_params(xref, dict, &p);
 
if (funcs > 0)
{
ncomp = 1;
pdf_sample_shade_function(shade, funcs, func, p.c0[0], p.c1[0]);
}
else
ncomp = shade->colorspace->n;
 
hasprevpatch = 0;
 
while (!fz_is_eof_bits(stream))
{
float c[4][FZ_MAX_COLORS];
fz_point v[12];
int startcolor;
int startpt;
int flag;
 
flag = fz_read_bits(stream, p.bpflag);
 
if (flag == 0)
{
startpt = 0;
startcolor = 0;
}
else
{
startpt = 4;
startcolor = 2;
}
 
for (i = startpt; i < 12; i++)
{
v[i].x = read_sample(stream, p.bpcoord, p.x0, p.x1);
v[i].y = read_sample(stream, p.bpcoord, p.y0, p.y1);
}
 
for (i = startcolor; i < 4; i++)
{
for (k = 0; k < ncomp; k++)
c[i][k] = read_sample(stream, p.bpcomp, p.c0[k], p.c1[k]);
}
 
haspatch = 0;
 
if (flag == 0)
{
haspatch = 1;
}
else if (flag == 1 && hasprevpatch)
{
v[0] = prevp[3];
v[1] = prevp[4];
v[2] = prevp[5];
v[3] = prevp[6];
memcpy(c[0], prevc[1], ncomp * sizeof(float));
memcpy(c[1], prevc[2], ncomp * sizeof(float));
 
haspatch = 1;
}
else if (flag == 2 && hasprevpatch)
{
v[0] = prevp[6];
v[1] = prevp[7];
v[2] = prevp[8];
v[3] = prevp[9];
memcpy(c[0], prevc[2], ncomp * sizeof(float));
memcpy(c[1], prevc[3], ncomp * sizeof(float));
 
haspatch = 1;
}
else if (flag == 3 && hasprevpatch)
{
v[0] = prevp[ 9];
v[1] = prevp[10];
v[2] = prevp[11];
v[3] = prevp[ 0];
memcpy(c[0], prevc[3], ncomp * sizeof(float));
memcpy(c[1], prevc[0], ncomp * sizeof(float));
 
haspatch = 1;
}
 
if (haspatch)
{
pdf_tensor_patch patch;
 
pdf_make_tensor_patch(&patch, 6, v);
 
for (i = 0; i < 4; i++)
memcpy(patch.color[i], c[i], ncomp * sizeof(float));
 
draw_patch(shade, &patch, SUBDIV, SUBDIV);
 
for (i = 0; i < 12; i++)
prevp[i] = v[i];
 
for (i = 0; i < 4; i++)
memcpy(prevc[i], c[i], ncomp * sizeof(float));
 
hasprevpatch = 1;
}
}
}
 
static void
pdf_load_type7_shade(fz_shade *shade, pdf_xref *xref, fz_obj *dict,
int funcs, pdf_function **func, fz_stream *stream)
{
struct mesh_params p;
int haspatch, hasprevpatch;
float prevc[4][FZ_MAX_COLORS];
fz_point prevp[16];
int ncomp;
int i, k;
 
pdf_load_mesh_params(xref, dict, &p);
 
if (funcs > 0)
{
ncomp = 1;
pdf_sample_shade_function(shade, funcs, func, p.c0[0], p.c1[0]);
}
else
ncomp = shade->colorspace->n;
 
hasprevpatch = 0;
 
while (!fz_is_eof_bits(stream))
{
float c[4][FZ_MAX_COLORS];
fz_point v[16];
int startcolor;
int startpt;
int flag;
 
flag = fz_read_bits(stream, p.bpflag);
 
if (flag == 0)
{
startpt = 0;
startcolor = 0;
}
else
{
startpt = 4;
startcolor = 2;
}
 
for (i = startpt; i < 16; i++)
{
v[i].x = read_sample(stream, p.bpcoord, p.x0, p.x1);
v[i].y = read_sample(stream, p.bpcoord, p.y0, p.y1);
}
 
for (i = startcolor; i < 4; i++)
{
for (k = 0; k < ncomp; k++)
c[i][k] = read_sample(stream, p.bpcomp, p.c0[k], p.c1[k]);
}
 
haspatch = 0;
 
if (flag == 0)
{
haspatch = 1;
}
else if (flag == 1 && hasprevpatch)
{
v[0] = prevp[3];
v[1] = prevp[4];
v[2] = prevp[5];
v[3] = prevp[6];
memcpy(c[0], prevc[1], ncomp * sizeof(float));
memcpy(c[1], prevc[2], ncomp * sizeof(float));
 
haspatch = 1;
}
else if (flag == 2 && hasprevpatch)
{
v[0] = prevp[6];
v[1] = prevp[7];
v[2] = prevp[8];
v[3] = prevp[9];
memcpy(c[0], prevc[2], ncomp * sizeof(float));
memcpy(c[1], prevc[3], ncomp * sizeof(float));
 
haspatch = 1;
}
else if (flag == 3 && hasprevpatch)
{
v[0] = prevp[ 9];
v[1] = prevp[10];
v[2] = prevp[11];
v[3] = prevp[ 0];
memcpy(c[0], prevc[3], ncomp * sizeof(float));
memcpy(c[1], prevc[0], ncomp * sizeof(float));
 
haspatch = 1;
}
 
if (haspatch)
{
pdf_tensor_patch patch;
 
pdf_make_tensor_patch(&patch, 7, v);
 
for (i = 0; i < 4; i++)
memcpy(patch.color[i], c[i], ncomp * sizeof(float));
 
draw_patch(shade, &patch, SUBDIV, SUBDIV);
 
for (i = 0; i < 16; i++)
prevp[i] = v[i];
 
for (i = 0; i < 4; i++)
memcpy(prevc[i], c[i], FZ_MAX_COLORS * sizeof(float));
 
hasprevpatch = 1;
}
}
}
 
/* Load all of the shading dictionary parameters, then switch on the shading type. */
 
static fz_error
pdf_load_shading_dict(fz_shade **shadep, pdf_xref *xref, fz_obj *dict, fz_matrix transform)
{
fz_error error;
fz_shade *shade;
pdf_function *func[FZ_MAX_COLORS] = { NULL };
fz_stream *stream = NULL;
fz_obj *obj;
int funcs;
int type;
int i;
 
shade = fz_malloc(sizeof(fz_shade));
shade->refs = 1;
shade->type = FZ_MESH;
shade->use_background = 0;
shade->use_function = 0;
shade->matrix = transform;
shade->bbox = fz_infinite_rect;
shade->extend[0] = 0;
shade->extend[1] = 0;
 
shade->mesh_len = 0;
shade->mesh_cap = 0;
shade->mesh = NULL;
 
shade->colorspace = NULL;
 
funcs = 0;
 
obj = fz_dict_gets(dict, "ShadingType");
type = fz_to_int(obj);
 
obj = fz_dict_gets(dict, "ColorSpace");
if (!obj)
{
fz_drop_shade(shade);
return fz_throw("shading colorspace is missing");
}
error = pdf_load_colorspace(&shade->colorspace, xref, obj);
if (error)
{
fz_drop_shade(shade);
return fz_rethrow(error, "cannot load colorspace (%d %d R)", fz_to_num(obj), fz_to_gen(obj));
}
 
obj = fz_dict_gets(dict, "Background");
if (obj)
{
shade->use_background = 1;
for (i = 0; i < shade->colorspace->n; i++)
shade->background[i] = fz_to_real(fz_array_get(obj, i));
}
 
obj = fz_dict_gets(dict, "BBox");
if (fz_is_array(obj))
{
shade->bbox = pdf_to_rect(obj);
}
 
obj = fz_dict_gets(dict, "Function");
if (fz_is_dict(obj))
{
funcs = 1;
 
error = pdf_load_function(&func[0], xref, obj);
if (error)
{
error = fz_rethrow(error, "cannot load shading function (%d %d R)", fz_to_num(obj), fz_to_gen(obj));
goto cleanup;
}
}
else if (fz_is_array(obj))
{
funcs = fz_array_len(obj);
if (funcs != 1 && funcs != shade->colorspace->n)
{
error = fz_throw("incorrect number of shading functions");
goto cleanup;
}
 
for (i = 0; i < funcs; i++)
{
error = pdf_load_function(&func[i], xref, fz_array_get(obj, i));
if (error)
{
error = fz_rethrow(error, "cannot load shading function (%d %d R)", fz_to_num(obj), fz_to_gen(obj));
goto cleanup;
}
}
}
 
if (type >= 4 && type <= 7)
{
error = pdf_open_stream(&stream, xref, fz_to_num(dict), fz_to_gen(dict));
if (error)
{
error = fz_rethrow(error, "cannot open shading stream (%d %d R)", fz_to_num(dict), fz_to_gen(dict));
goto cleanup;
}
}
 
switch (type)
{
case 1: pdf_load_function_based_shading(shade, xref, dict, func[0]); break;
case 2: pdf_load_axial_shading(shade, xref, dict, funcs, func); break;
case 3: pdf_load_radial_shading(shade, xref, dict, funcs, func); break;
case 4: pdf_load_type4_shade(shade, xref, dict, funcs, func, stream); break;
case 5: pdf_load_type5_shade(shade, xref, dict, funcs, func, stream); break;
case 6: pdf_load_type6_shade(shade, xref, dict, funcs, func, stream); break;
case 7: pdf_load_type7_shade(shade, xref, dict, funcs, func, stream); break;
default:
error = fz_throw("unknown shading type: %d", type);
goto cleanup;
}
 
if (stream)
fz_close(stream);
for (i = 0; i < funcs; i++)
if (func[i])
pdf_drop_function(func[i]);
 
*shadep = shade;
return fz_okay;
 
cleanup:
if (stream)
fz_close(stream);
for (i = 0; i < funcs; i++)
if (func[i])
pdf_drop_function(func[i]);
fz_drop_shade(shade);
 
return fz_rethrow(error, "cannot load shading type %d (%d %d R)", type, fz_to_num(dict), fz_to_gen(dict));
}
 
fz_error
pdf_load_shading(fz_shade **shadep, pdf_xref *xref, fz_obj *dict)
{
fz_error error;
fz_matrix mat;
fz_obj *obj;
 
if ((*shadep = pdf_find_item(xref->store, fz_drop_shade, dict)))
{
fz_keep_shade(*shadep);
return fz_okay;
}
 
/* Type 2 pattern dictionary */
if (fz_dict_gets(dict, "PatternType"))
{
obj = fz_dict_gets(dict, "Matrix");
if (obj)
mat = pdf_to_matrix(obj);
else
mat = fz_identity;
 
obj = fz_dict_gets(dict, "ExtGState");
if (obj)
{
if (fz_dict_gets(obj, "CA") || fz_dict_gets(obj, "ca"))
{
fz_warn("shading with alpha not supported");
}
}
 
obj = fz_dict_gets(dict, "Shading");
if (!obj)
return fz_throw("syntaxerror: missing shading dictionary");
 
error = pdf_load_shading_dict(shadep, xref, obj, mat);
if (error)
return fz_rethrow(error, "cannot load shading dictionary (%d %d R)", fz_to_num(obj), fz_to_gen(obj));
}
 
/* Naked shading dictionary */
else
{
error = pdf_load_shading_dict(shadep, xref, dict, fz_identity);
if (error)
return fz_rethrow(error, "cannot load shading dictionary (%d %d R)", fz_to_num(dict), fz_to_gen(dict));
}
 
pdf_store_item(xref->store, fz_keep_shade, fz_drop_shade, dict, *shadep);
 
return fz_okay;
}
/contrib/media/updf/pdf/pdf_store.c
0,0 → 1,222
#include "fitz.h"
#include "mupdf.h"
 
typedef struct pdf_item_s pdf_item;
 
struct pdf_item_s
{
void *drop_func;
fz_obj *key;
void *val;
int age;
pdf_item *next;
};
 
struct refkey
{
void *drop_func;
int num;
int gen;
};
 
struct pdf_store_s
{
fz_hash_table *hash; /* hash for num/gen keys */
pdf_item *root; /* linked list for everything else */
};
 
pdf_store *
pdf_new_store(void)
{
pdf_store *store;
store = fz_malloc(sizeof(pdf_store));
store->hash = fz_new_hash_table(4096, sizeof(struct refkey));
store->root = NULL;
return store;
}
 
void
pdf_store_item(pdf_store *store, void *keepfunc, void *drop_func, fz_obj *key, void *val)
{
pdf_item *item;
 
if (!store)
return;
 
item = fz_malloc(sizeof(pdf_item));
item->drop_func = drop_func;
item->key = fz_keep_obj(key);
item->val = ((void*(*)(void*))keepfunc)(val);
item->age = 0;
item->next = NULL;
 
if (fz_is_indirect(key))
{
struct refkey refkey;
refkey.drop_func = drop_func;
refkey.num = fz_to_num(key);
refkey.gen = fz_to_gen(key);
fz_hash_insert(store->hash, &refkey, item);
}
else
{
item->next = store->root;
store->root = item;
}
}
 
void *
pdf_find_item(pdf_store *store, void *drop_func, fz_obj *key)
{
struct refkey refkey;
pdf_item *item;
 
if (!store)
return NULL;
 
if (key == NULL)
return NULL;
 
if (fz_is_indirect(key))
{
refkey.drop_func = drop_func;
refkey.num = fz_to_num(key);
refkey.gen = fz_to_gen(key);
item = fz_hash_find(store->hash, &refkey);
if (item)
{
item->age = 0;
return item->val;
}
}
else
{
for (item = store->root; item; item = item->next)
{
if (item->drop_func == drop_func && !fz_objcmp(item->key, key))
{
item->age = 0;
return item->val;
}
}
}
 
return NULL;
}
 
void
pdf_remove_item(pdf_store *store, void *drop_func, fz_obj *key)
{
struct refkey refkey;
pdf_item *item, *prev, *next;
 
if (fz_is_indirect(key))
{
refkey.drop_func = drop_func;
refkey.num = fz_to_num(key);
refkey.gen = fz_to_gen(key);
item = fz_hash_find(store->hash, &refkey);
if (item)
{
fz_hash_remove(store->hash, &refkey);
((void(*)(void*))item->drop_func)(item->val);
fz_drop_obj(item->key);
fz_free(item);
}
}
else
{
prev = NULL;
for (item = store->root; item; item = next)
{
next = item->next;
if (item->drop_func == drop_func && !fz_objcmp(item->key, key))
{
if (!prev)
store->root = next;
else
prev->next = next;
((void(*)(void*))item->drop_func)(item->val);
fz_drop_obj(item->key);
fz_free(item);
}
else
prev = item;
}
}
}
 
void
pdf_age_store(pdf_store *store, int maxage)
{
struct refkey *refkey;
pdf_item *item, *prev, *next;
int i;
 
for (i = 0; i < fz_hash_len(store->hash); i++)
{
refkey = fz_hash_get_key(store->hash, i);
item = fz_hash_get_val(store->hash, i);
if (item && ++item->age > maxage)
{
fz_hash_remove(store->hash, refkey);
((void(*)(void*))item->drop_func)(item->val);
fz_drop_obj(item->key);
fz_free(item);
i--; /* items with same hash may move into place */
}
}
 
prev = NULL;
for (item = store->root; item; item = next)
{
next = item->next;
if (++item->age > maxage)
{
if (!prev)
store->root = next;
else
prev->next = next;
((void(*)(void*))item->drop_func)(item->val);
fz_drop_obj(item->key);
fz_free(item);
}
else
prev = item;
}
}
 
void
pdf_free_store(pdf_store *store)
{
pdf_age_store(store, 0);
fz_free_hash(store->hash);
fz_free(store);
}
 
void
pdf_debug_store(pdf_store *store)
{
pdf_item *item;
pdf_item *next;
struct refkey *refkey;
int i;
 
printf("-- resource store contents --\n");
 
for (i = 0; i < fz_hash_len(store->hash); i++)
{
refkey = fz_hash_get_key(store->hash, i);
item = fz_hash_get_val(store->hash, i);
if (item)
printf("store[%d] (%d %d R) = %p\n", i, refkey->num, refkey->gen, item->val);
}
 
for (item = store->root; item; item = next)
{
next = item->next;
printf("store[*] ");
fz_debug_obj(item->key);
printf(" = %p\n", item->val);
}
}
/contrib/media/updf/pdf/pdf_stream.c
0,0 → 1,385
#include "fitz.h"
#include "mupdf.h"
 
/*
* Check if an object is a stream or not.
*/
int
pdf_is_stream(pdf_xref *xref, int num, int gen)
{
fz_error error;
 
if (num < 0 || num >= xref->len)
return 0;
 
error = pdf_cache_object(xref, num, gen);
if (error)
{
fz_catch(error, "cannot load object, ignoring error");
return 0;
}
 
return xref->table[num].stm_ofs > 0;
}
 
/*
* Scan stream dictionary for an explicit /Crypt filter
*/
static int
pdf_stream_has_crypt(fz_obj *stm)
{
fz_obj *filters;
fz_obj *obj;
int i;
 
filters = fz_dict_getsa(stm, "Filter", "F");
if (filters)
{
if (!strcmp(fz_to_name(filters), "Crypt"))
return 1;
if (fz_is_array(filters))
{
for (i = 0; i < fz_array_len(filters); i++)
{
obj = fz_array_get(filters, i);
if (!strcmp(fz_to_name(obj), "Crypt"))
return 1;
}
}
}
return 0;
}
 
/*
* Create a filter given a name and param dictionary.
*/
static fz_stream *
build_filter(fz_stream *chain, pdf_xref * xref, fz_obj * f, fz_obj * p, int num, int gen)
{
fz_error error;
char *s;
 
s = fz_to_name(f);
 
if (!strcmp(s, "ASCIIHexDecode") || !strcmp(s, "AHx"))
return fz_open_ahxd(chain);
 
else if (!strcmp(s, "ASCII85Decode") || !strcmp(s, "A85"))
return fz_open_a85d(chain);
 
else if (!strcmp(s, "CCITTFaxDecode") || !strcmp(s, "CCF"))
return fz_open_faxd(chain, p);
 
else if (!strcmp(s, "DCTDecode") || !strcmp(s, "DCT"))
return fz_open_dctd(chain, p);
 
else if (!strcmp(s, "RunLengthDecode") || !strcmp(s, "RL"))
return fz_open_rld(chain);
 
else if (!strcmp(s, "FlateDecode") || !strcmp(s, "Fl"))
{
fz_obj *obj = fz_dict_gets(p, "Predictor");
if (fz_to_int(obj) > 1)
return fz_open_predict(fz_open_flated(chain), p);
return fz_open_flated(chain);
}
 
else if (!strcmp(s, "LZWDecode") || !strcmp(s, "LZW"))
{
fz_obj *obj = fz_dict_gets(p, "Predictor");
if (fz_to_int(obj) > 1)
return fz_open_predict(fz_open_lzwd(chain, p), p);
return fz_open_lzwd(chain, p);
}
 
else if (!strcmp(s, "JBIG2Decode"))
{
fz_obj *obj = fz_dict_gets(p, "JBIG2Globals");
if (obj)
{
fz_buffer *globals;
error = pdf_load_stream(&globals, xref, fz_to_num(obj), fz_to_gen(obj));
if (error)
fz_catch(error, "cannot load jbig2 global segments");
chain = fz_open_jbig2d(chain, globals);
fz_drop_buffer(globals);
return chain;
}
return fz_open_jbig2d(chain, NULL);
}
 
else if (!strcmp(s, "JPXDecode"))
return chain; /* JPX decoding is special cased in the image loading code */
 
else if (!strcmp(s, "Crypt"))
{
fz_obj *name;
 
if (!xref->crypt)
{
fz_warn("crypt filter in unencrypted document");
return chain;
}
 
name = fz_dict_gets(p, "Name");
if (fz_is_name(name))
return pdf_open_crypt_with_filter(chain, xref->crypt, fz_to_name(name), num, gen);
 
return chain;
}
 
fz_warn("unknown filter name (%s)", s);
return chain;
}
 
/*
* Build a chain of filters given filter names and param dicts.
* If head is given, start filter chain with it.
* Assume ownership of head.
*/
static fz_stream *
build_filter_chain(fz_stream *chain, pdf_xref *xref, fz_obj *fs, fz_obj *ps, int num, int gen)
{
fz_obj *f;
fz_obj *p;
int i;
 
for (i = 0; i < fz_array_len(fs); i++)
{
f = fz_array_get(fs, i);
p = fz_array_get(ps, i);
chain = build_filter(chain, xref, f, p, num, gen);
}
 
return chain;
}
 
/*
* Build a filter for reading raw stream data.
* This is a null filter to constrain reading to the
* stream length, followed by a decryption filter.
*/
static fz_stream *
pdf_open_raw_filter(fz_stream *chain, pdf_xref *xref, fz_obj *stmobj, int num, int gen)
{
int hascrypt;
int len;
 
/* don't close chain when we close this filter */
fz_keep_stream(chain);
 
len = fz_to_int(fz_dict_gets(stmobj, "Length"));
chain = fz_open_null(chain, len);
 
hascrypt = pdf_stream_has_crypt(stmobj);
if (xref->crypt && !hascrypt)
chain = pdf_open_crypt(chain, xref->crypt, num, gen);
 
return chain;
}
 
/*
* Construct a filter to decode a stream, constraining
* to stream length and decrypting.
*/
static fz_stream *
pdf_open_filter(fz_stream *chain, pdf_xref *xref, fz_obj *stmobj, int num, int gen)
{
fz_obj *filters;
fz_obj *params;
 
filters = fz_dict_getsa(stmobj, "Filter", "F");
params = fz_dict_getsa(stmobj, "DecodeParms", "DP");
 
chain = pdf_open_raw_filter(chain, xref, stmobj, num, gen);
 
if (fz_is_name(filters))
return build_filter(chain, xref, filters, params, num, gen);
if (fz_array_len(filters) > 0)
return build_filter_chain(chain, xref, filters, params, num, gen);
 
return chain;
}
 
/*
* Construct a filter to decode a stream, without
* constraining to stream length, and without decryption.
*/
fz_stream *
pdf_open_inline_stream(fz_stream *chain, pdf_xref *xref, fz_obj *stmobj, int length)
{
fz_obj *filters;
fz_obj *params;
 
filters = fz_dict_getsa(stmobj, "Filter", "F");
params = fz_dict_getsa(stmobj, "DecodeParms", "DP");
 
/* don't close chain when we close this filter */
fz_keep_stream(chain);
 
if (fz_is_name(filters))
return build_filter(chain, xref, filters, params, 0, 0);
if (fz_array_len(filters) > 0)
return build_filter_chain(chain, xref, filters, params, 0, 0);
 
return fz_open_null(chain, length);
}
 
/*
* Open a stream for reading the raw (compressed but decrypted) data.
* Using xref->file while this is open is a bad idea.
*/
fz_error
pdf_open_raw_stream(fz_stream **stmp, pdf_xref *xref, int num, int gen)
{
pdf_xref_entry *x;
fz_error error;
 
if (num < 0 || num >= xref->len)
return fz_throw("object id out of range (%d %d R)", num, gen);
 
x = xref->table + num;
 
error = pdf_cache_object(xref, num, gen);
if (error)
return fz_rethrow(error, "cannot load stream object (%d %d R)", num, gen);
 
if (x->stm_ofs)
{
*stmp = pdf_open_raw_filter(xref->file, xref, x->obj, num, gen);
fz_seek(xref->file, x->stm_ofs, 0);
return fz_okay;
}
 
return fz_throw("object is not a stream");
}
 
/*
* Open a stream for reading uncompressed data.
* Put the opened file in xref->stream.
* Using xref->file while a stream is open is a Bad idea.
*/
fz_error
pdf_open_stream(fz_stream **stmp, pdf_xref *xref, int num, int gen)
{
pdf_xref_entry *x;
fz_error error;
 
if (num < 0 || num >= xref->len)
return fz_throw("object id out of range (%d %d R)", num, gen);
 
x = xref->table + num;
 
error = pdf_cache_object(xref, num, gen);
if (error)
return fz_rethrow(error, "cannot load stream object (%d %d R)", num, gen);
 
if (x->stm_ofs)
{
*stmp = pdf_open_filter(xref->file, xref, x->obj, num, gen);
fz_seek(xref->file, x->stm_ofs, 0);
return fz_okay;
}
 
return fz_throw("object is not a stream");
}
 
fz_error
pdf_open_stream_at(fz_stream **stmp, pdf_xref *xref, int num, int gen, fz_obj *dict, int stm_ofs)
{
if (stm_ofs)
{
*stmp = pdf_open_filter(xref->file, xref, dict, num, gen);
fz_seek(xref->file, stm_ofs, 0);
return fz_okay;
}
return fz_throw("object is not a stream");
}
 
/*
* Load raw (compressed but decrypted) contents of a stream into buf.
*/
fz_error
pdf_load_raw_stream(fz_buffer **bufp, pdf_xref *xref, int num, int gen)
{
fz_error error;
fz_stream *stm;
fz_obj *dict;
int len;
 
error = pdf_load_object(&dict, xref, num, gen);
if (error)
return fz_rethrow(error, "cannot load stream dictionary (%d %d R)", num, gen);
 
len = fz_to_int(fz_dict_gets(dict, "Length"));
 
fz_drop_obj(dict);
 
error = pdf_open_raw_stream(&stm, xref, num, gen);
if (error)
return fz_rethrow(error, "cannot open raw stream (%d %d R)", num, gen);
 
error = fz_read_all(bufp, stm, len);
if (error)
{
fz_close(stm);
return fz_rethrow(error, "cannot read raw stream (%d %d R)", num, gen);
}
 
fz_close(stm);
return fz_okay;
}
 
static int
pdf_guess_filter_length(int len, char *filter)
{
if (!strcmp(filter, "ASCIIHexDecode"))
return len / 2;
if (!strcmp(filter, "ASCII85Decode"))
return len * 4 / 5;
if (!strcmp(filter, "FlateDecode"))
return len * 3;
if (!strcmp(filter, "RunLengthDecode"))
return len * 3;
if (!strcmp(filter, "LZWDecode"))
return len * 2;
return len;
}
 
/*
* Load uncompressed contents of a stream into buf.
*/
fz_error
pdf_load_stream(fz_buffer **bufp, pdf_xref *xref, int num, int gen)
{
fz_error error;
fz_stream *stm;
fz_obj *dict, *obj;
int i, len;
 
error = pdf_open_stream(&stm, xref, num, gen);
if (error)
return fz_rethrow(error, "cannot open stream (%d %d R)", num, gen);
 
error = pdf_load_object(&dict, xref, num, gen);
if (error)
return fz_rethrow(error, "cannot load stream dictionary (%d %d R)", num, gen);
 
len = fz_to_int(fz_dict_gets(dict, "Length"));
obj = fz_dict_gets(dict, "Filter");
len = pdf_guess_filter_length(len, fz_to_name(obj));
for (i = 0; i < fz_array_len(obj); i++)
len = pdf_guess_filter_length(len, fz_to_name(fz_array_get(obj, i)));
 
fz_drop_obj(dict);
 
error = fz_read_all(bufp, stm, len);
if (error)
{
fz_close(stm);
return fz_rethrow(error, "cannot read raw stream (%d %d R)", num, gen);
}
 
fz_close(stm);
return fz_okay;
}
/contrib/media/updf/pdf/pdf_type3.c
0,0 → 1,158
#include "fitz.h"
#include "mupdf.h"
 
static fz_error
pdf_run_glyph_func(void *xref, fz_obj *rdb, fz_buffer *contents, fz_device *dev, fz_matrix ctm)
{
return pdf_run_glyph(xref, rdb, contents, dev, ctm);
}
 
fz_error
pdf_load_type3_font(pdf_font_desc **fontdescp, pdf_xref *xref, fz_obj *rdb, fz_obj *dict)
{
fz_error error;
char buf[256];
char *estrings[256];
pdf_font_desc *fontdesc;
fz_obj *encoding;
fz_obj *widths;
fz_obj *charprocs;
fz_obj *obj;
int first, last;
int i, k, n;
fz_rect bbox;
fz_matrix matrix;
 
obj = fz_dict_gets(dict, "Name");
if (fz_is_name(obj))
fz_strlcpy(buf, fz_to_name(obj), sizeof buf);
else
sprintf(buf, "Unnamed-T3");
 
fontdesc = pdf_new_font_desc();
 
obj = fz_dict_gets(dict, "FontMatrix");
matrix = pdf_to_matrix(obj);
 
obj = fz_dict_gets(dict, "FontBBox");
bbox = pdf_to_rect(obj);
 
fontdesc->font = fz_new_type3_font(buf, matrix);
 
fz_set_font_bbox(fontdesc->font, bbox.x0, bbox.y0, bbox.x1, bbox.y1);
 
/* Encoding */
 
for (i = 0; i < 256; i++)
estrings[i] = NULL;
 
encoding = fz_dict_gets(dict, "Encoding");
if (!encoding)
{
error = fz_throw("syntaxerror: Type3 font missing Encoding");
goto cleanup;
}
 
if (fz_is_name(encoding))
pdf_load_encoding(estrings, fz_to_name(encoding));
 
if (fz_is_dict(encoding))
{
fz_obj *base, *diff, *item;
 
base = fz_dict_gets(encoding, "BaseEncoding");
if (fz_is_name(base))
pdf_load_encoding(estrings, fz_to_name(base));
 
diff = fz_dict_gets(encoding, "Differences");
if (fz_is_array(diff))
{
n = fz_array_len(diff);
k = 0;
for (i = 0; i < n; i++)
{
item = fz_array_get(diff, i);
if (fz_is_int(item))
k = fz_to_int(item);
if (fz_is_name(item))
estrings[k++] = fz_to_name(item);
if (k < 0) k = 0;
if (k > 255) k = 255;
}
}
}
 
fontdesc->encoding = pdf_new_identity_cmap(0, 1);
 
error = pdf_load_to_unicode(fontdesc, xref, estrings, NULL, fz_dict_gets(dict, "ToUnicode"));
if (error)
goto cleanup;
 
/* Widths */
 
pdf_set_default_hmtx(fontdesc, 0);
 
first = fz_to_int(fz_dict_gets(dict, "FirstChar"));
last = fz_to_int(fz_dict_gets(dict, "LastChar"));
 
widths = fz_dict_gets(dict, "Widths");
if (!widths)
{
error = fz_throw("syntaxerror: Type3 font missing Widths");
goto cleanup;
}
 
for (i = first; i <= last; i++)
{
float w = fz_to_real(fz_array_get(widths, i - first));
w = fontdesc->font->t3matrix.a * w * 1000;
fontdesc->font->t3widths[i] = w * 0.001f;
pdf_add_hmtx(fontdesc, i, i, w);
}
 
pdf_end_hmtx(fontdesc);
 
/* Resources -- inherit page resources if the font doesn't have its own */
 
fontdesc->font->t3resources = fz_dict_gets(dict, "Resources");
if (!fontdesc->font->t3resources)
fontdesc->font->t3resources = rdb;
if (fontdesc->font->t3resources)
fz_keep_obj(fontdesc->font->t3resources);
if (!fontdesc->font->t3resources)
fz_warn("no resource dictionary for type 3 font!");
 
fontdesc->font->t3xref = xref;
fontdesc->font->t3run = pdf_run_glyph_func;
 
/* CharProcs */
 
charprocs = fz_dict_gets(dict, "CharProcs");
if (!charprocs)
{
error = fz_throw("syntaxerror: Type3 font missing CharProcs");
goto cleanup;
}
 
for (i = 0; i < 256; i++)
{
if (estrings[i])
{
obj = fz_dict_gets(charprocs, estrings[i]);
if (pdf_is_stream(xref, fz_to_num(obj), fz_to_gen(obj)))
{
error = pdf_load_stream(&fontdesc->font->t3procs[i], xref, fz_to_num(obj), fz_to_gen(obj));
if (error)
goto cleanup;
}
}
}
 
*fontdescp = fontdesc;
return fz_okay;
 
cleanup:
fz_drop_font(fontdesc->font);
fz_free(fontdesc);
return fz_rethrow(error, "cannot load type3 font (%d %d R)", fz_to_num(dict), fz_to_gen(dict));
}
/contrib/media/updf/pdf/pdf_unicode.c
0,0 → 1,83
#include "fitz.h"
#include "mupdf.h"
 
/* Load or synthesize ToUnicode map for fonts */
 
fz_error
pdf_load_to_unicode(pdf_font_desc *font, pdf_xref *xref,
char **strings, char *collection, fz_obj *cmapstm)
{
fz_error error = fz_okay;
pdf_cmap *cmap;
int cid;
int ucsbuf[8];
int ucslen;
int i;
 
if (pdf_is_stream(xref, fz_to_num(cmapstm), fz_to_gen(cmapstm)))
{
error = pdf_load_embedded_cmap(&cmap, xref, cmapstm);
if (error)
return fz_rethrow(error, "cannot load embedded cmap (%d %d R)", fz_to_num(cmapstm), fz_to_gen(cmapstm));
 
font->to_unicode = pdf_new_cmap();
 
for (i = 0; i < (strings ? 256 : 65536); i++)
{
cid = pdf_lookup_cmap(font->encoding, i);
if (cid >= 0)
{
ucslen = pdf_lookup_cmap_full(cmap, i, ucsbuf);
if (ucslen == 1)
pdf_map_range_to_range(font->to_unicode, cid, cid, ucsbuf[0]);
if (ucslen > 1)
pdf_map_one_to_many(font->to_unicode, cid, ucsbuf, ucslen);
}
}
 
pdf_sort_cmap(font->to_unicode);
 
pdf_drop_cmap(cmap);
}
 
else if (collection)
{
error = fz_okay;
 
if (!strcmp(collection, "Adobe-CNS1"))
error = pdf_load_system_cmap(&font->to_unicode, "Adobe-CNS1-UCS2");
else if (!strcmp(collection, "Adobe-GB1"))
error = pdf_load_system_cmap(&font->to_unicode, "Adobe-GB1-UCS2");
else if (!strcmp(collection, "Adobe-Japan1"))
error = pdf_load_system_cmap(&font->to_unicode, "Adobe-Japan1-UCS2");
else if (!strcmp(collection, "Adobe-Korea1"))
error = pdf_load_system_cmap(&font->to_unicode, "Adobe-Korea1-UCS2");
 
if (error)
return fz_rethrow(error, "cannot load ToUnicode system cmap %s-UCS2", collection);
}
 
if (strings)
{
/* TODO one-to-many mappings */
 
font->cid_to_ucs_len = 256;
font->cid_to_ucs = fz_calloc(256, sizeof(unsigned short));
 
for (i = 0; i < 256; i++)
{
if (strings[i])
font->cid_to_ucs[i] = pdf_lookup_agl(strings[i]);
else
font->cid_to_ucs[i] = '?';
}
}
 
if (!font->to_unicode && !font->cid_to_ucs)
{
/* TODO: synthesize a ToUnicode if it's a freetype font with
* cmap and/or post tables or if it has glyph names. */
}
 
return fz_okay;
}
/contrib/media/updf/pdf/pdf_xobject.c
0,0 → 1,96
#include "fitz.h"
#include "mupdf.h"
 
fz_error
pdf_load_xobject(pdf_xobject **formp, pdf_xref *xref, fz_obj *dict)
{
fz_error error;
pdf_xobject *form;
fz_obj *obj;
 
if ((*formp = pdf_find_item(xref->store, pdf_drop_xobject, dict)))
{
pdf_keep_xobject(*formp);
return fz_okay;
}
 
form = fz_malloc(sizeof(pdf_xobject));
form->refs = 1;
form->resources = NULL;
form->contents = NULL;
form->colorspace = NULL;
 
/* Store item immediately, to avoid possible recursion if objects refer back to this one */
pdf_store_item(xref->store, pdf_keep_xobject, pdf_drop_xobject, dict, form);
 
obj = fz_dict_gets(dict, "BBox");
form->bbox = pdf_to_rect(obj);
 
obj = fz_dict_gets(dict, "Matrix");
if (obj)
form->matrix = pdf_to_matrix(obj);
else
form->matrix = fz_identity;
 
form->isolated = 0;
form->knockout = 0;
form->transparency = 0;
 
obj = fz_dict_gets(dict, "Group");
if (obj)
{
fz_obj *attrs = obj;
 
form->isolated = fz_to_bool(fz_dict_gets(attrs, "I"));
form->knockout = fz_to_bool(fz_dict_gets(attrs, "K"));
 
obj = fz_dict_gets(attrs, "S");
if (fz_is_name(obj) && !strcmp(fz_to_name(obj), "Transparency"))
form->transparency = 1;
 
obj = fz_dict_gets(attrs, "CS");
if (obj)
{
error = pdf_load_colorspace(&form->colorspace, xref, obj);
if (error)
fz_catch(error, "cannot load xobject colorspace");
}
}
 
form->resources = fz_dict_gets(dict, "Resources");
if (form->resources)
fz_keep_obj(form->resources);
 
error = pdf_load_stream(&form->contents, xref, fz_to_num(dict), fz_to_gen(dict));
if (error)
{
pdf_remove_item(xref->store, pdf_drop_xobject, dict);
pdf_drop_xobject(form);
return fz_rethrow(error, "cannot load xobject content stream (%d %d R)", fz_to_num(dict), fz_to_gen(dict));
}
 
*formp = form;
return fz_okay;
}
 
pdf_xobject *
pdf_keep_xobject(pdf_xobject *xobj)
{
xobj->refs ++;
return xobj;
}
 
void
pdf_drop_xobject(pdf_xobject *xobj)
{
if (xobj && --xobj->refs == 0)
{
if (xobj->colorspace)
fz_drop_colorspace(xobj->colorspace);
if (xobj->resources)
fz_drop_obj(xobj->resources);
if (xobj->contents)
fz_drop_buffer(xobj->contents);
fz_free(xobj);
}
}
/contrib/media/updf/pdf/pdf_xref.c
0,0 → 1,940
#include "fitz.h"
#include "mupdf.h"
 
static inline int iswhite(int ch)
{
return
ch == '\000' || ch == '\011' || ch == '\012' ||
ch == '\014' || ch == '\015' || ch == '\040';
}
 
/*
* magic version tag and startxref
*/
 
static fz_error
pdf_load_version(pdf_xref *xref)
{
char buf[20];
 
fz_seek(xref->file, 0, 0);
fz_read_line(xref->file, buf, sizeof buf);
if (memcmp(buf, "%PDF-", 5) != 0)
return fz_throw("cannot recognize version marker");
 
xref->version = atoi(buf + 5) * 10 + atoi(buf + 7);
 
return fz_okay;
}
 
static fz_error
pdf_read_start_xref(pdf_xref *xref)
{
unsigned char buf[1024];
int t, n;
int i;
 
fz_seek(xref->file, 0, 2);
 
xref->file_size = fz_tell(xref->file);
 
t = MAX(0, xref->file_size - (int)sizeof buf);
fz_seek(xref->file, t, 0);
 
n = fz_read(xref->file, buf, sizeof buf);
if (n < 0)
return fz_rethrow(n, "cannot read from file");
 
for (i = n - 9; i >= 0; i--)
{
if (memcmp(buf + i, "startxref", 9) == 0)
{
i += 9;
while (iswhite(buf[i]) && i < n)
i ++;
xref->startxref = atoi((char*)(buf + i));
return fz_okay;
}
}
 
return fz_throw("cannot find startxref");
}
 
/*
* trailer dictionary
*/
 
static fz_error
pdf_read_old_trailer(pdf_xref *xref, char *buf, int cap)
{
fz_error error;
int len;
char *s;
int n;
int t;
int tok;
int c;
 
fz_read_line(xref->file, buf, cap);
if (strncmp(buf, "xref", 4) != 0)
return fz_throw("cannot find xref marker");
 
while (1)
{
c = fz_peek_byte(xref->file);
if (!(c >= '0' && c <= '9'))
break;
 
fz_read_line(xref->file, buf, cap);
s = buf;
fz_strsep(&s, " "); /* ignore ofs */
if (!s)
return fz_throw("invalid range marker in xref");
len = atoi(fz_strsep(&s, " "));
 
/* broken pdfs where the section is not on a separate line */
if (s && *s != '\0')
fz_seek(xref->file, -(2 + (int)strlen(s)), 1);
 
t = fz_tell(xref->file);
if (t < 0)
return fz_throw("cannot tell in file");
 
fz_seek(xref->file, t + 20 * len, 0);
}
 
error = pdf_lex(&tok, xref->file, buf, cap, &n);
if (error)
return fz_rethrow(error, "cannot parse trailer");
if (tok != PDF_TOK_TRAILER)
return fz_throw("expected trailer marker");
 
error = pdf_lex(&tok, xref->file, buf, cap, &n);
if (error)
return fz_rethrow(error, "cannot parse trailer");
if (tok != PDF_TOK_OPEN_DICT)
return fz_throw("expected trailer dictionary");
 
error = pdf_parse_dict(&xref->trailer, xref, xref->file, buf, cap);
if (error)
return fz_rethrow(error, "cannot parse trailer");
return fz_okay;
}
 
static fz_error
pdf_read_new_trailer(pdf_xref *xref, char *buf, int cap)
{
fz_error error;
error = pdf_parse_ind_obj(&xref->trailer, xref, xref->file, buf, cap, NULL, NULL, NULL);
if (error)
return fz_rethrow(error, "cannot parse trailer (compressed)");
return fz_okay;
}
 
static fz_error
pdf_read_trailer(pdf_xref *xref, char *buf, int cap)
{
fz_error error;
int c;
 
fz_seek(xref->file, xref->startxref, 0);
 
while (iswhite(fz_peek_byte(xref->file)))
fz_read_byte(xref->file);
 
c = fz_peek_byte(xref->file);
if (c == 'x')
{
error = pdf_read_old_trailer(xref, buf, cap);
if (error)
return fz_rethrow(error, "cannot read trailer");
}
else if (c >= '0' && c <= '9')
{
error = pdf_read_new_trailer(xref, buf, cap);
if (error)
return fz_rethrow(error, "cannot read trailer");
}
else
{
return fz_throw("cannot recognize xref format: '%c'", c);
}
 
return fz_okay;
}
 
/*
* xref tables
*/
 
void
pdf_resize_xref(pdf_xref *xref, int newlen)
{
int i;
 
xref->table = fz_realloc(xref->table, newlen, sizeof(pdf_xref_entry));
for (i = xref->len; i < newlen; i++)
{
xref->table[i].type = 0;
xref->table[i].ofs = 0;
xref->table[i].gen = 0;
xref->table[i].stm_ofs = 0;
xref->table[i].obj = NULL;
}
xref->len = newlen;
}
 
static fz_error
pdf_read_old_xref(fz_obj **trailerp, pdf_xref *xref, char *buf, int cap)
{
fz_error error;
int ofs, len;
char *s;
int n;
int tok;
int i;
int c;
 
fz_read_line(xref->file, buf, cap);
if (strncmp(buf, "xref", 4) != 0)
return fz_throw("cannot find xref marker");
 
while (1)
{
c = fz_peek_byte(xref->file);
if (!(c >= '0' && c <= '9'))
break;
 
fz_read_line(xref->file, buf, cap);
s = buf;
ofs = atoi(fz_strsep(&s, " "));
len = atoi(fz_strsep(&s, " "));
 
/* broken pdfs where the section is not on a separate line */
if (s && *s != '\0')
{
fz_warn("broken xref section. proceeding anyway.");
fz_seek(xref->file, -(2 + (int)strlen(s)), 1);
}
 
/* broken pdfs where size in trailer undershoots entries in xref sections */
if (ofs + len > xref->len)
{
fz_warn("broken xref section, proceeding anyway.");
pdf_resize_xref(xref, ofs + len);
}
 
for (i = ofs; i < ofs + len; i++)
{
n = fz_read(xref->file, (unsigned char *) buf, 20);
if (n < 0)
return fz_rethrow(n, "cannot read xref table");
if (!xref->table[i].type)
{
s = buf;
 
/* broken pdfs where line start with white space */
while (*s != '\0' && iswhite(*s))
s++;
 
xref->table[i].ofs = atoi(s);
xref->table[i].gen = atoi(s + 11);
xref->table[i].type = s[17];
if (s[17] != 'f' && s[17] != 'n' && s[17] != 'o')
return fz_throw("unexpected xref type: %#x (%d %d R)", s[17], i, xref->table[i].gen);
}
}
}
 
error = pdf_lex(&tok, xref->file, buf, cap, &n);
if (error)
return fz_rethrow(error, "cannot parse trailer");
if (tok != PDF_TOK_TRAILER)
return fz_throw("expected trailer marker");
 
error = pdf_lex(&tok, xref->file, buf, cap, &n);
if (error)
return fz_rethrow(error, "cannot parse trailer");
if (tok != PDF_TOK_OPEN_DICT)
return fz_throw("expected trailer dictionary");
 
error = pdf_parse_dict(trailerp, xref, xref->file, buf, cap);
if (error)
return fz_rethrow(error, "cannot parse trailer");
return fz_okay;
}
 
static fz_error
pdf_read_new_xref_section(pdf_xref *xref, fz_stream *stm, int i0, int i1, int w0, int w1, int w2)
{
int i, n;
 
if (i0 < 0 || i0 + i1 > xref->len)
return fz_throw("xref stream has too many entries");
 
for (i = i0; i < i0 + i1; i++)
{
int a = 0;
int b = 0;
int c = 0;
 
if (fz_is_eof(stm))
return fz_throw("truncated xref stream");
 
for (n = 0; n < w0; n++)
a = (a << 8) + fz_read_byte(stm);
for (n = 0; n < w1; n++)
b = (b << 8) + fz_read_byte(stm);
for (n = 0; n < w2; n++)
c = (c << 8) + fz_read_byte(stm);
 
if (!xref->table[i].type)
{
int t = w0 ? a : 1;
xref->table[i].type = t == 0 ? 'f' : t == 1 ? 'n' : t == 2 ? 'o' : 0;
xref->table[i].ofs = w1 ? b : 0;
xref->table[i].gen = w2 ? c : 0;
}
}
 
return fz_okay;
}
 
static fz_error
pdf_read_new_xref(fz_obj **trailerp, pdf_xref *xref, char *buf, int cap)
{
fz_error error;
fz_stream *stm;
fz_obj *trailer;
fz_obj *index;
fz_obj *obj;
int num, gen, stm_ofs;
int size, w0, w1, w2;
int t;
 
error = pdf_parse_ind_obj(&trailer, xref, xref->file, buf, cap, &num, &gen, &stm_ofs);
if (error)
return fz_rethrow(error, "cannot parse compressed xref stream object");
 
obj = fz_dict_gets(trailer, "Size");
if (!obj)
{
fz_drop_obj(trailer);
return fz_throw("xref stream missing Size entry (%d %d R)", num, gen);
}
size = fz_to_int(obj);
 
if (size > xref->len)
{
pdf_resize_xref(xref, size);
}
 
if (num < 0 || num >= xref->len)
{
fz_drop_obj(trailer);
return fz_throw("object id (%d %d R) out of range (0..%d)", num, gen, xref->len - 1);
}
 
obj = fz_dict_gets(trailer, "W");
if (!obj) {
fz_drop_obj(trailer);
return fz_throw("xref stream missing W entry (%d %d R)", num, gen);
}
w0 = fz_to_int(fz_array_get(obj, 0));
w1 = fz_to_int(fz_array_get(obj, 1));
w2 = fz_to_int(fz_array_get(obj, 2));
 
index = fz_dict_gets(trailer, "Index");
 
error = pdf_open_stream_at(&stm, xref, num, gen, trailer, stm_ofs);
if (error)
{
fz_drop_obj(trailer);
return fz_rethrow(error, "cannot open compressed xref stream (%d %d R)", num, gen);
}
 
if (!index)
{
error = pdf_read_new_xref_section(xref, stm, 0, size, w0, w1, w2);
if (error)
{
fz_close(stm);
fz_drop_obj(trailer);
return fz_rethrow(error, "cannot read xref stream (%d %d R)", num, gen);
}
}
else
{
for (t = 0; t < fz_array_len(index); t += 2)
{
int i0 = fz_to_int(fz_array_get(index, t + 0));
int i1 = fz_to_int(fz_array_get(index, t + 1));
error = pdf_read_new_xref_section(xref, stm, i0, i1, w0, w1, w2);
if (error)
{
fz_close(stm);
fz_drop_obj(trailer);
return fz_rethrow(error, "cannot read xref stream section (%d %d R)", num, gen);
}
}
}
 
fz_close(stm);
 
*trailerp = trailer;
 
return fz_okay;
}
 
static fz_error
pdf_read_xref(fz_obj **trailerp, pdf_xref *xref, int ofs, char *buf, int cap)
{
fz_error error;
int c;
 
fz_seek(xref->file, ofs, 0);
 
while (iswhite(fz_peek_byte(xref->file)))
fz_read_byte(xref->file);
 
c = fz_peek_byte(xref->file);
if (c == 'x')
{
error = pdf_read_old_xref(trailerp, xref, buf, cap);
if (error)
return fz_rethrow(error, "cannot read xref (ofs=%d)", ofs);
}
else if (c >= '0' && c <= '9')
{
error = pdf_read_new_xref(trailerp, xref, buf, cap);
if (error)
return fz_rethrow(error, "cannot read xref (ofs=%d)", ofs);
}
else
{
return fz_throw("cannot recognize xref format");
}
 
return fz_okay;
}
 
static fz_error
pdf_read_xref_sections(pdf_xref *xref, int ofs, char *buf, int cap)
{
fz_error error;
fz_obj *trailer;
fz_obj *prev;
fz_obj *xrefstm;
 
error = pdf_read_xref(&trailer, xref, ofs, buf, cap);
if (error)
return fz_rethrow(error, "cannot read xref section");
 
/* FIXME: do we overwrite free entries properly? */
xrefstm = fz_dict_gets(trailer, "XRefStm");
if (xrefstm)
{
error = pdf_read_xref_sections(xref, fz_to_int(xrefstm), buf, cap);
if (error)
{
fz_drop_obj(trailer);
return fz_rethrow(error, "cannot read /XRefStm xref section");
}
}
 
prev = fz_dict_gets(trailer, "Prev");
if (prev)
{
error = pdf_read_xref_sections(xref, fz_to_int(prev), buf, cap);
if (error)
{
fz_drop_obj(trailer);
return fz_rethrow(error, "cannot read /Prev xref section");
}
}
 
fz_drop_obj(trailer);
return fz_okay;
}
 
/*
* load xref tables from pdf
*/
 
static fz_error
pdf_load_xref(pdf_xref *xref, char *buf, int bufsize)
{
fz_error error;
fz_obj *size;
int i;
 
error = pdf_load_version(xref);
if (error)
return fz_rethrow(error, "cannot read version marker");
 
error = pdf_read_start_xref(xref);
if (error)
return fz_rethrow(error, "cannot read startxref");
 
error = pdf_read_trailer(xref, buf, bufsize);
if (error)
return fz_rethrow(error, "cannot read trailer");
 
size = fz_dict_gets(xref->trailer, "Size");
if (!size)
return fz_throw("trailer missing Size entry");
 
pdf_resize_xref(xref, fz_to_int(size));
 
error = pdf_read_xref_sections(xref, xref->startxref, buf, bufsize);
if (error)
return fz_rethrow(error, "cannot read xref");
 
/* broken pdfs where first object is not free */
if (xref->table[0].type != 'f')
return fz_throw("first object in xref is not free");
 
/* broken pdfs where object offsets are out of range */
for (i = 0; i < xref->len; i++)
{
if (xref->table[i].type == 'n')
if (xref->table[i].ofs <= 0 || xref->table[i].ofs >= xref->file_size)
return fz_throw("object offset out of range: %d (%d 0 R)", xref->table[i].ofs, i);
if (xref->table[i].type == 'o')
if (xref->table[i].ofs <= 0 || xref->table[i].ofs >= xref->len || xref->table[xref->table[i].ofs].type != 'n')
return fz_throw("invalid reference to an objstm that does not exist: %d (%d 0 R)", xref->table[i].ofs, i);
}
 
return fz_okay;
}
 
/*
* Initialize and load xref tables.
* If password is not null, try to decrypt.
*/
 
fz_error
pdf_open_xref_with_stream(pdf_xref **xrefp, fz_stream *file, char *password)
{
pdf_xref *xref;
fz_error error;
fz_obj *encrypt, *id;
fz_obj *dict, *obj;
int i, repaired = 0;
 
/* install pdf specific callback */
fz_resolve_indirect = pdf_resolve_indirect;
 
xref = fz_malloc(sizeof(pdf_xref));
 
memset(xref, 0, sizeof(pdf_xref));
 
xref->file = fz_keep_stream(file);
 
error = pdf_load_xref(xref, xref->scratch, sizeof xref->scratch);
if (error)
{
fz_catch(error, "trying to repair");
if (xref->table)
{
fz_free(xref->table);
xref->table = NULL;
xref->len = 0;
}
if (xref->trailer)
{
fz_drop_obj(xref->trailer);
xref->trailer = NULL;
}
error = pdf_repair_xref(xref, xref->scratch, sizeof xref->scratch);
if (error)
{
pdf_free_xref(xref);
return fz_rethrow(error, "cannot repair document");
}
repaired = 1;
}
 
encrypt = fz_dict_gets(xref->trailer, "Encrypt");
id = fz_dict_gets(xref->trailer, "ID");
if (fz_is_dict(encrypt))
{
error = pdf_new_crypt(&xref->crypt, encrypt, id);
if (error)
{
pdf_free_xref(xref);
return fz_rethrow(error, "cannot decrypt document");
}
}
 
if (pdf_needs_password(xref))
{
/* Only care if we have a password */
if (password)
{
int okay = pdf_authenticate_password(xref, password);
if (!okay)
{
pdf_free_xref(xref);
return fz_throw("invalid password");
}
}
}
 
if (repaired)
{
int hasroot, hasinfo;
 
error = pdf_repair_obj_stms(xref);
if (error)
{
pdf_free_xref(xref);
return fz_rethrow(error, "cannot repair document");
}
 
hasroot = fz_dict_gets(xref->trailer, "Root") != NULL;
hasinfo = fz_dict_gets(xref->trailer, "Info") != NULL;
 
for (i = 1; i < xref->len; i++)
{
if (xref->table[i].type == 0 || xref->table[i].type == 'f')
continue;
 
error = pdf_load_object(&dict, xref, i, 0);
if (error)
{
fz_catch(error, "ignoring broken object (%d 0 R)", i);
continue;
}
 
if (!hasroot)
{
obj = fz_dict_gets(dict, "Type");
if (fz_is_name(obj) && !strcmp(fz_to_name(obj), "Catalog"))
{
obj = fz_new_indirect(i, 0, xref);
fz_dict_puts(xref->trailer, "Root", obj);
fz_drop_obj(obj);
}
}
 
if (!hasinfo)
{
if (fz_dict_gets(dict, "Creator") || fz_dict_gets(dict, "Producer"))
{
obj = fz_new_indirect(i, 0, xref);
fz_dict_puts(xref->trailer, "Info", obj);
fz_drop_obj(obj);
}
}
 
fz_drop_obj(dict);
}
}
 
*xrefp = xref;
return fz_okay;
}
 
void
pdf_free_xref(pdf_xref *xref)
{
int i;
 
if (xref->store)
pdf_free_store(xref->store);
 
if (xref->table)
{
for (i = 0; i < xref->len; i++)
{
if (xref->table[i].obj)
{
fz_drop_obj(xref->table[i].obj);
xref->table[i].obj = NULL;
}
}
fz_free(xref->table);
}
 
if (xref->page_objs)
{
for (i = 0; i < xref->page_len; i++)
fz_drop_obj(xref->page_objs[i]);
fz_free(xref->page_objs);
}
 
if (xref->page_refs)
{
for (i = 0; i < xref->page_len; i++)
fz_drop_obj(xref->page_refs[i]);
fz_free(xref->page_refs);
}
 
if (xref->file)
fz_close(xref->file);
if (xref->trailer)
fz_drop_obj(xref->trailer);
if (xref->crypt)
pdf_free_crypt(xref->crypt);
 
fz_free(xref);
}
 
void
pdf_debug_xref(pdf_xref *xref)
{
int i;
printf("xref\n0 %d\n", xref->len);
for (i = 0; i < xref->len; i++)
{
printf("%05d: %010d %05d %c (stm_ofs=%d)\n", i,
xref->table[i].ofs,
xref->table[i].gen,
xref->table[i].type ? xref->table[i].type : '-',
xref->table[i].stm_ofs);
}
}
 
/*
* compressed object streams
*/
 
static fz_error
pdf_load_obj_stm(pdf_xref *xref, int num, int gen, char *buf, int cap)
{
fz_error error;
fz_stream *stm;
fz_obj *objstm;
int *numbuf;
int *ofsbuf;
 
fz_obj *obj;
int first;
int count;
int i, n;
int tok;
 
error = pdf_load_object(&objstm, xref, num, gen);
if (error)
return fz_rethrow(error, "cannot load object stream object (%d %d R)", num, gen);
 
count = fz_to_int(fz_dict_gets(objstm, "N"));
first = fz_to_int(fz_dict_gets(objstm, "First"));
 
numbuf = fz_calloc(count, sizeof(int));
ofsbuf = fz_calloc(count, sizeof(int));
 
error = pdf_open_stream(&stm, xref, num, gen);
if (error)
{
error = fz_rethrow(error, "cannot open object stream (%d %d R)", num, gen);
goto cleanupbuf;
}
 
for (i = 0; i < count; i++)
{
error = pdf_lex(&tok, stm, buf, cap, &n);
if (error || tok != PDF_TOK_INT)
{
error = fz_rethrow(error, "corrupt object stream (%d %d R)", num, gen);
goto cleanupstm;
}
numbuf[i] = atoi(buf);
 
error = pdf_lex(&tok, stm, buf, cap, &n);
if (error || tok != PDF_TOK_INT)
{
error = fz_rethrow(error, "corrupt object stream (%d %d R)", num, gen);
goto cleanupstm;
}
ofsbuf[i] = atoi(buf);
}
 
fz_seek(stm, first, 0);
 
for (i = 0; i < count; i++)
{
fz_seek(stm, first + ofsbuf[i], 0);
 
error = pdf_parse_stm_obj(&obj, xref, stm, buf, cap);
if (error)
{
error = fz_rethrow(error, "cannot parse object %d in stream (%d %d R)", i, num, gen);
goto cleanupstm;
}
 
if (numbuf[i] < 1 || numbuf[i] >= xref->len)
{
fz_drop_obj(obj);
error = fz_throw("object id (%d 0 R) out of range (0..%d)", numbuf[i], xref->len - 1);
goto cleanupstm;
}
 
if (xref->table[numbuf[i]].type == 'o' && xref->table[numbuf[i]].ofs == num)
{
if (xref->table[numbuf[i]].obj)
fz_drop_obj(xref->table[numbuf[i]].obj);
xref->table[numbuf[i]].obj = obj;
}
else
{
fz_drop_obj(obj);
}
}
 
fz_close(stm);
fz_free(ofsbuf);
fz_free(numbuf);
fz_drop_obj(objstm);
return fz_okay;
 
cleanupstm:
fz_close(stm);
cleanupbuf:
fz_free(ofsbuf);
fz_free(numbuf);
fz_drop_obj(objstm);
return error; /* already rethrown */
}
 
/*
* object loading
*/
 
fz_error
pdf_cache_object(pdf_xref *xref, int num, int gen)
{
fz_error error;
pdf_xref_entry *x;
int rnum, rgen;
 
if (num < 0 || num >= xref->len)
return fz_throw("object out of range (%d %d R); xref size %d", num, gen, xref->len);
 
x = &xref->table[num];
 
if (x->obj)
return fz_okay;
 
if (x->type == 'f')
{
x->obj = fz_new_null();
return fz_okay;
}
else if (x->type == 'n')
{
fz_seek(xref->file, x->ofs, 0);
 
error = pdf_parse_ind_obj(&x->obj, xref, xref->file, xref->scratch, sizeof xref->scratch,
&rnum, &rgen, &x->stm_ofs);
if (error)
return fz_rethrow(error, "cannot parse object (%d %d R)", num, gen);
 
if (rnum != num)
return fz_throw("found object (%d %d R) instead of (%d %d R)", rnum, rgen, num, gen);
 
if (xref->crypt)
pdf_crypt_obj(xref->crypt, x->obj, num, gen);
}
else if (x->type == 'o')
{
if (!x->obj)
{
error = pdf_load_obj_stm(xref, x->ofs, 0, xref->scratch, sizeof xref->scratch);
if (error)
return fz_rethrow(error, "cannot load object stream containing object (%d %d R)", num, gen);
if (!x->obj)
return fz_throw("object (%d %d R) was not found in its object stream", num, gen);
}
}
else
{
return fz_throw("assert: corrupt xref struct");
}
 
return fz_okay;
}
 
fz_error
pdf_load_object(fz_obj **objp, pdf_xref *xref, int num, int gen)
{
fz_error error;
 
error = pdf_cache_object(xref, num, gen);
if (error)
return fz_rethrow(error, "cannot load object (%d %d R) into cache", num, gen);
 
assert(xref->table[num].obj);
 
*objp = fz_keep_obj(xref->table[num].obj);
 
return fz_okay;
}
 
fz_obj *
pdf_resolve_indirect(fz_obj *ref)
{
if (fz_is_indirect(ref))
{
pdf_xref *xref = fz_get_indirect_xref(ref);
int num = fz_to_num(ref);
int gen = fz_to_gen(ref);
if (xref)
{
fz_error error = pdf_cache_object(xref, num, gen);
if (error)
{
fz_catch(error, "cannot load object (%d %d R) into cache", num, gen);
return ref;
}
if (xref->table[num].obj)
return xref->table[num].obj;
}
}
return ref;
}
 
/* Replace numbered object -- for use by pdfclean and similar tools */
void
pdf_update_object(pdf_xref *xref, int num, int gen, fz_obj *newobj)
{
pdf_xref_entry *x;
 
if (num < 0 || num >= xref->len)
{
fz_warn("object out of range (%d %d R); xref size %d", num, gen, xref->len);
return;
}
 
x = &xref->table[num];
 
if (x->obj)
fz_drop_obj(x->obj);
 
x->obj = fz_keep_obj(newobj);
x->type = 'n';
x->ofs = 0;
}
 
/*
* Convenience function to open a file then call pdf_open_xref_with_stream.
*/
 
fz_error
pdf_open_xref(pdf_xref **xrefp, const char *filename, char *password)
{
fz_error error;
fz_stream *file;
 
file = fz_open_file(filename);
if (!file)
return fz_throw("cannot open file '%s': %s", filename, strerror(errno));
 
error = pdf_open_xref_with_stream(xrefp, file, password);
if (error)
return fz_rethrow(error, "cannot load document '%s'", filename);
 
fz_close(file);
return fz_okay;
}
/contrib/media/updf/pdf/snprintf.c
0,0 → 1,1025
/*
* snprintf.c - a portable implementation of snprintf
*
* AUTHOR
* Mark Martinec <mark.martinec@ijs.si>, April 1999.
*
* Copyright 1999, Mark Martinec. All rights reserved.
*
* TERMS AND CONDITIONS
* This program is free software; you can redistribute it and/or modify
* it under the terms of the "Frontier Artistic License" which comes
* with this Kit.
*
* This program 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 Frontier Artistic License for more details.
*
* You should have received a copy of the Frontier Artistic License
* with this Kit in the file named LICENSE.txt .
* If not, I'll be glad to provide one.
*
* FEATURES
* - careful adherence to specs regarding flags, field width and precision;
* - good performance for large string handling (large format, large
* argument or large paddings). Performance is similar to system's sprintf
* and in several cases significantly better (make sure you compile with
* optimizations turned on, tell the compiler the code is strict ANSI
* if necessary to give it more freedom for optimizations);
* - return value semantics per ISO/IEC 9899:1999 ("ISO C99");
* - written in standard ISO/ANSI C - requires an ANSI C compiler.
*
* SUPPORTED CONVERSION SPECIFIERS AND DATA TYPES
*
* This snprintf only supports the following conversion specifiers:
* s, c, d, u, o, x, X, p (and synonyms: i, D, U, O - see below)
* with flags: '-', '+', ' ', '0' and '#'.
* An asterisk is supported for field width as well as precision.
*
* Length modifiers 'h' (short int), 'l' (long int),
* and 'll' (long long int) are supported.
* NOTE:
* If macro SNPRINTF_LONGLONG_SUPPORT is not defined (default) the
* length modifier 'll' is recognized but treated the same as 'l',
* which may cause argument value truncation! Defining
* SNPRINTF_LONGLONG_SUPPORT requires that your system's sprintf also
* handles length modifier 'll'. long long int is a language extension
* which may not be portable.
*
* Conversion of numeric data (conversion specifiers d, u, o, x, X, p)
* with length modifiers (none or h, l, ll) is left to the system routine
* sprintf, but all handling of flags, field width and precision as well as
* c and s conversions is done very carefully by this portable routine.
* If a string precision (truncation) is specified (e.g. %.8s) it is
* guaranteed the string beyond the specified precision will not be referenced.
*
* Length modifiers h, l and ll are ignored for c and s conversions (data
* types wint_t and wchar_t are not supported).
*
* The following common synonyms for conversion characters are supported:
* - i is a synonym for d
* - D is a synonym for ld, explicit length modifiers are ignored
* - U is a synonym for lu, explicit length modifiers are ignored
* - O is a synonym for lo, explicit length modifiers are ignored
* The D, O and U conversion characters are nonstandard, they are supported
* for backward compatibility only, and should not be used for new code.
*
* The following is specifically NOT supported:
* - flag ' (thousands' grouping character) is recognized but ignored
* - numeric conversion specifiers: f, e, E, g, G and synonym F,
* as well as the new a and A conversion specifiers
* - length modifier 'L' (long double) and 'q' (quad - use 'll' instead)
* - wide character/string conversions: lc, ls, and nonstandard
* synonyms C and S
* - writeback of converted string length: conversion character n
* - the n$ specification for direct reference to n-th argument
* - locales
*
* It is permitted for str_m to be zero, and it is permitted to specify NULL
* pointer for resulting string argument if str_m is zero (as per ISO C99).
*
* The return value is the number of characters which would be generated
* for the given input, excluding the trailing null. If this value
* is greater or equal to str_m, not all characters from the result
* have been stored in str, output bytes beyond the (str_m-1) -th character
* are discarded. If str_m is greater than zero it is guaranteed
* the resulting string will be null-terminated.
*
* NOTE that this matches the ISO C99, OpenBSD, and GNU C library 2.1,
* but is different from some older and vendor implementations,
* and is also different from XPG, XSH5, SUSv2 specifications.
* For historical discussion on changes in the semantics and standards
* of snprintf see printf(3) man page in the Linux programmers manual.
*
* Routines asprintf and vasprintf return a pointer (in the ptr argument)
* to a buffer sufficiently large to hold the resulting string. This pointer
* should be passed to free(3) to release the allocated storage when it is
* no longer needed. If sufficient space cannot be allocated, these functions
* will return -1 and set ptr to be a NULL pointer. These two routines are a
* GNU C library extensions (glibc).
*
* Routines asnprintf and vasnprintf are similar to asprintf and vasprintf,
* yet, like snprintf and vsnprintf counterparts, will write at most str_m-1
* characters into the allocated output string, the last character in the
* allocated buffer then gets the terminating null. If the formatted string
* length (the return value) is greater than or equal to the str_m argument,
* the resulting string was truncated and some of the formatted characters
* were discarded. These routines present a handy way to limit the amount
* of allocated memory to some sane value.
*
* AVAILABILITY
* http://www.ijs.si/software/snprintf/
*
* REVISION HISTORY
* 1999-04 V0.9 Mark Martinec
* - initial version, some modifications after comparing printf
* man pages for Digital Unix 4.0, Solaris 2.6 and HPUX 10,
* and checking how Perl handles sprintf (differently!);
* 1999-04-09 V1.0 Mark Martinec <mark.martinec@ijs.si>
* - added main test program, fixed remaining inconsistencies,
* added optional (long long int) support;
* 1999-04-12 V1.1 Mark Martinec <mark.martinec@ijs.si>
* - support the 'p' conversion (pointer to void);
* - if a string precision is specified
* make sure the string beyond the specified precision
* will not be referenced (e.g. by strlen);
* 1999-04-13 V1.2 Mark Martinec <mark.martinec@ijs.si>
* - support synonyms %D=%ld, %U=%lu, %O=%lo;
* - speed up the case of long format string with few conversions;
* 1999-06-30 V1.3 Mark Martinec <mark.martinec@ijs.si>
* - fixed runaway loop (eventually crashing when str_l wraps
* beyond 2^31) while copying format string without
* conversion specifiers to a buffer that is too short
* (thanks to Edwin Young <edwiny@autonomy.com> for
* spotting the problem);
* - added macros PORTABLE_SNPRINTF_VERSION_(MAJOR|MINOR)
* to snprintf.h
* 2000-02-14 V2.0 (never released) Mark Martinec <mark.martinec@ijs.si>
* - relaxed license terms: The Artistic License now applies.
* You may still apply the GNU GENERAL PUBLIC LICENSE
* as was distributed with previous versions, if you prefer;
* - changed REVISION HISTORY dates to use ISO 8601 date format;
* - added vsnprintf (patch also independently proposed by
* Caolan McNamara 2000-05-04, and Keith M Willenson 2000-06-01)
* 2000-06-27 V2.1 Mark Martinec <mark.martinec@ijs.si>
* - removed POSIX check for str_m<1; value 0 for str_m is
* allowed by ISO C99 (and GNU C library 2.1) - (pointed out
* on 2000-05-04 by Caolan McNamara, caolan@ csn dot ul dot ie).
* Besides relaxed license this change in standards adherence
* is the main reason to bump up the major version number;
* - added nonstandard routines asnprintf, vasnprintf, asprintf,
* vasprintf that dynamically allocate storage for the
* resulting string; these routines are not compiled by default,
* see comments where NEED_V?ASN?PRINTF macros are defined;
* - autoconf contributed by Caolan McNamara
* 2000-10-06 V2.2 Mark Martinec <mark.martinec@ijs.si>
* - BUG FIX: the %c conversion used a temporary variable
* that was no longer in scope when referenced,
* possibly causing incorrect resulting character;
* - BUG FIX: make precision and minimal field width unsigned
* to handle huge values (2^31 <= n < 2^32) correctly;
* also be more careful in the use of signed/unsigned/size_t
* internal variables - probably more careful than many
* vendor implementations, but there may still be a case
* where huge values of str_m, precision or minimal field
* could cause incorrect behaviour;
* - use separate variables for signed/unsigned arguments,
* and for short/int, long, and long long argument lengths
* to avoid possible incompatibilities on certain
* computer architectures. Also use separate variable
* arg_sign to hold sign of a numeric argument,
* to make code more transparent;
* - some fiddling with zero padding and "0x" to make it
* Linux compatible;
* - systematically use macros fast_memcpy and fast_memset
* instead of case-by-case hand optimization; determine some
* breakeven string lengths for different architectures;
* - terminology change: 'format' -> 'conversion specifier',
* 'C9x' -> 'ISO/IEC 9899:1999 ("ISO C99")',
* 'alternative form' -> 'alternate form',
* 'data type modifier' -> 'length modifier';
* - several comments rephrased and new ones added;
* - make compiler not complain about 'credits' defined but
* not used;
*/
 
 
/* Define HAVE_SNPRINTF if your system already has snprintf and vsnprintf.
*
* If HAVE_SNPRINTF is defined this module will not produce code for
* snprintf and vsnprintf, unless PREFER_PORTABLE_SNPRINTF is defined as well,
* causing this portable version of snprintf to be called portable_snprintf
* (and portable_vsnprintf).
*/
/* #define HAVE_SNPRINTF */
 
/* Define PREFER_PORTABLE_SNPRINTF if your system does have snprintf and
* vsnprintf but you would prefer to use the portable routine(s) instead.
* In this case the portable routine is declared as portable_snprintf
* (and portable_vsnprintf) and a macro 'snprintf' (and 'vsnprintf')
* is defined to expand to 'portable_v?snprintf' - see file snprintf.h .
* Defining this macro is only useful if HAVE_SNPRINTF is also defined,
* but does does no harm if defined nevertheless.
*/
/* #define PREFER_PORTABLE_SNPRINTF */
 
/* Define SNPRINTF_LONGLONG_SUPPORT if you want to support
* data type (long long int) and length modifier 'll' (e.g. %lld).
* If undefined, 'll' is recognized but treated as a single 'l'.
*
* If the system's sprintf does not handle 'll'
* the SNPRINTF_LONGLONG_SUPPORT must not be defined!
*
* This is off by default as (long long int) is a language extension.
*/
/* #define SNPRINTF_LONGLONG_SUPPORT */
 
/* Define NEED_SNPRINTF_ONLY if you only need snprintf, and not vsnprintf.
* If NEED_SNPRINTF_ONLY is defined, the snprintf will be defined directly,
* otherwise both snprintf and vsnprintf routines will be defined
* and snprintf will be a simple wrapper around vsnprintf, at the expense
* of an extra procedure call.
*/
/* #define NEED_SNPRINTF_ONLY */
 
/* Define NEED_V?ASN?PRINTF macros if you need library extension
* routines asprintf, vasprintf, asnprintf, vasnprintf respectively,
* and your system library does not provide them. They are all small
* wrapper routines around portable_vsnprintf. Defining any of the four
* NEED_V?ASN?PRINTF macros automatically turns off NEED_SNPRINTF_ONLY
* and turns on PREFER_PORTABLE_SNPRINTF.
*
* Watch for name conflicts with the system library if these routines
* are already present there.
*
* NOTE: vasprintf and vasnprintf routines need va_copy() from stdarg.h, as
* specified by C99, to be able to traverse the same list of arguments twice.
* I don't know of any other standard and portable way of achieving the same.
* With some versions of gcc you may use __va_copy(). You might even get away
* with "ap2 = ap", in this case you must not call va_end(ap2) !
* #define va_copy(ap2,ap) ap2 = ap
*/
/* #define NEED_ASPRINTF */
/* #define NEED_ASNPRINTF */
/* #define NEED_VASPRINTF */
/* #define NEED_VASNPRINTF */
 
 
/* Define the following macros if desired:
* SOLARIS_COMPATIBLE, SOLARIS_BUG_COMPATIBLE,
* HPUX_COMPATIBLE, HPUX_BUG_COMPATIBLE, LINUX_COMPATIBLE,
* DIGITAL_UNIX_COMPATIBLE, DIGITAL_UNIX_BUG_COMPATIBLE,
* PERL_COMPATIBLE, PERL_BUG_COMPATIBLE,
*
* - For portable applications it is best not to rely on peculiarities
* of a given implementation so it may be best not to define any
* of the macros that select compatibility and to avoid features
* that vary among the systems.
*
* - Selecting compatibility with more than one operating system
* is not strictly forbidden but is not recommended.
*
* - 'x'_BUG_COMPATIBLE implies 'x'_COMPATIBLE .
*
* - 'x'_COMPATIBLE refers to (and enables) a behaviour that is
* documented in a sprintf man page on a given operating system
* and actually adhered to by the system's sprintf (but not on
* most other operating systems). It may also refer to and enable
* a behaviour that is declared 'undefined' or 'implementation specific'
* in the man page but a given implementation behaves predictably
* in a certain way.
*
* - 'x'_BUG_COMPATIBLE refers to (and enables) a behaviour of system's sprintf
* that contradicts the sprintf man page on the same operating system.
*
* - I do not claim that the 'x'_COMPATIBLE and 'x'_BUG_COMPATIBLE
* conditionals take into account all idiosyncrasies of a particular
* implementation, there may be other incompatibilities.
*/
 
 
/* ============================================= */
/* NO USER SERVICABLE PARTS FOLLOWING THIS POINT */
/* ============================================= */
 
#define PORTABLE_SNPRINTF_VERSION_MAJOR 2
#define PORTABLE_SNPRINTF_VERSION_MINOR 2
 
#if defined(NEED_ASPRINTF) || defined(NEED_ASNPRINTF) || defined(NEED_VASPRINTF) || defined(NEED_VASNPRINTF)
# if defined(NEED_SNPRINTF_ONLY)
# undef NEED_SNPRINTF_ONLY
# endif
# if !defined(PREFER_PORTABLE_SNPRINTF)
# define PREFER_PORTABLE_SNPRINTF
# endif
#endif
 
#if defined(SOLARIS_BUG_COMPATIBLE) && !defined(SOLARIS_COMPATIBLE)
#define SOLARIS_COMPATIBLE
#endif
 
#if defined(HPUX_BUG_COMPATIBLE) && !defined(HPUX_COMPATIBLE)
#define HPUX_COMPATIBLE
#endif
 
#if defined(DIGITAL_UNIX_BUG_COMPATIBLE) && !defined(DIGITAL_UNIX_COMPATIBLE)
#define DIGITAL_UNIX_COMPATIBLE
#endif
 
#if defined(PERL_BUG_COMPATIBLE) && !defined(PERL_COMPATIBLE)
#define PERL_COMPATIBLE
#endif
 
#if defined(LINUX_BUG_COMPATIBLE) && !defined(LINUX_COMPATIBLE)
#define LINUX_COMPATIBLE
#endif
 
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
#include <errno.h>
 
#ifdef isdigit
#undef isdigit
#endif
#define isdigit(c) ((c) >= '0' && (c) <= '9')
 
/* For copying strings longer or equal to 'breakeven_point'
* it is more efficient to call memcpy() than to do it inline.
* The value depends mostly on the processor architecture,
* but also on the compiler and its optimization capabilities.
* The value is not critical, some small value greater than zero
* will be just fine if you don't care to squeeze every drop
* of performance out of the code.
*
* Small values favor memcpy, large values favor inline code.
*/
#if defined(__alpha__) || defined(__alpha)
# define breakeven_point 2 /* AXP (DEC Alpha) - gcc or cc or egcs */
#endif
#if defined(__i386__) || defined(__i386)
# define breakeven_point 12 /* Intel Pentium/Linux - gcc 2.96 */
#endif
#if defined(__hppa)
# define breakeven_point 10 /* HP-PA - gcc */
#endif
#if defined(__sparc__) || defined(__sparc)
# define breakeven_point 33 /* Sun Sparc 5 - gcc 2.8.1 */
#endif
 
/* some other values of possible interest: */
/* #define breakeven_point 8 */ /* VAX 4000 - vaxc */
/* #define breakeven_point 19 */ /* VAX 4000 - gcc 2.7.0 */
 
#ifndef breakeven_point
# define breakeven_point 6 /* some reasonable one-size-fits-all value */
#endif
 
#define fast_memcpy(d,s,n) \
{ register size_t nn = (size_t)(n); \
if (nn >= breakeven_point) memcpy((d), (s), nn); \
else if (nn > 0) { /* proc call overhead is worth only for large strings*/\
register char *dd; register const char *ss; \
for (ss=(s), dd=(d); nn>0; nn--) *dd++ = *ss++; } }
 
#define fast_memset(d,c,n) \
{ register size_t nn = (size_t)(n); \
if (nn >= breakeven_point) memset((d), (int)(c), nn); \
else if (nn > 0) { /* proc call overhead is worth only for large strings*/\
register char *dd; register const int cc=(int)(c); \
for (dd=(d); nn>0; nn--) *dd++ = cc; } }
 
/* prototypes */
 
#if defined(NEED_ASPRINTF)
int asprintf (char **ptr, const char *fmt, /*args*/ ...);
#endif
#if defined(NEED_VASPRINTF)
int vasprintf (char **ptr, const char *fmt, va_list ap);
#endif
#if defined(NEED_ASNPRINTF)
int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...);
#endif
#if defined(NEED_VASNPRINTF)
int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap);
#endif
 
#if defined(HAVE_SNPRINTF)
/* declare our portable snprintf routine under name portable_snprintf */
/* declare our portable vsnprintf routine under name portable_vsnprintf */
#else
/* declare our portable routines under names snprintf and vsnprintf */
#define portable_snprintf snprintf
#if !defined(NEED_SNPRINTF_ONLY)
#define portable_vsnprintf vsnprintf
#endif
#endif
 
#if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...);
#if !defined(NEED_SNPRINTF_ONLY)
int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap);
#endif
#endif
 
/* declarations */
 
static char credits[] = "\n\
@(#)snprintf.c, v2.2: Mark Martinec, <mark.martinec@ijs.si>\n\
@(#)snprintf.c, v2.2: Copyright 1999, Mark Martinec. Frontier Artistic License applies.\n\
@(#)snprintf.c, v2.2: http://www.ijs.si/software/snprintf/\n";
 
#if defined(NEED_ASPRINTF)
int asprintf(char **ptr, const char *fmt, /*args*/ ...) {
va_list ap;
size_t str_m;
int str_l;
 
*ptr = NULL;
va_start(ap, fmt); /* measure the required size */
str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap);
va_end(ap);
assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */
*ptr = (char *) malloc(str_m = (size_t)str_l + 1);
if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
else {
int str_l2;
va_start(ap, fmt);
str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
va_end(ap);
assert(str_l2 == str_l);
}
return str_l;
}
#endif
 
#if defined(NEED_VASPRINTF)
int vasprintf(char **ptr, const char *fmt, va_list ap) {
size_t str_m;
int str_l;
 
*ptr = NULL;
{ va_list ap2;
va_copy(ap2, ap); /* don't consume the original ap, we'll need it again */
str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/
va_end(ap2);
}
assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */
*ptr = (char *) malloc(str_m = (size_t)str_l + 1);
if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
else {
int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
assert(str_l2 == str_l);
}
return str_l;
}
#endif
 
#if defined(NEED_ASNPRINTF)
int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...) {
va_list ap;
int str_l;
 
*ptr = NULL;
va_start(ap, fmt); /* measure the required size */
str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap);
va_end(ap);
assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */
if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1; /* truncate */
/* if str_m is 0, no buffer is allocated, just set *ptr to NULL */
if (str_m == 0) { /* not interested in resulting string, just return size */
} else {
*ptr = (char *) malloc(str_m);
if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
else {
int str_l2;
va_start(ap, fmt);
str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
va_end(ap);
assert(str_l2 == str_l);
}
}
return str_l;
}
#endif
 
#if defined(NEED_VASNPRINTF)
int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap) {
int str_l;
 
*ptr = NULL;
{ va_list ap2;
va_copy(ap2, ap); /* don't consume the original ap, we'll need it again */
str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/
va_end(ap2);
}
assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */
if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1; /* truncate */
/* if str_m is 0, no buffer is allocated, just set *ptr to NULL */
if (str_m == 0) { /* not interested in resulting string, just return size */
} else {
*ptr = (char *) malloc(str_m);
if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
else {
int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
assert(str_l2 == str_l);
}
}
return str_l;
}
#endif
 
/*
* If the system does have snprintf and the portable routine is not
* specifically required, this module produces no code for snprintf/vsnprintf.
*/
#if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
 
#if !defined(NEED_SNPRINTF_ONLY)
int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) {
va_list ap;
int str_l;
 
va_start(ap, fmt);
str_l = portable_vsnprintf(str, str_m, fmt, ap);
va_end(ap);
return str_l;
}
#endif
 
#if defined(NEED_SNPRINTF_ONLY)
int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) {
#else
int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap) {
#endif
 
#if defined(NEED_SNPRINTF_ONLY)
va_list ap;
#endif
size_t str_l = 0;
const char *p = fmt;
 
/* In contrast with POSIX, the ISO C99 now says
* that str can be NULL and str_m can be 0.
* This is more useful than the old: if (str_m < 1) return -1; */
 
#if defined(NEED_SNPRINTF_ONLY)
va_start(ap, fmt);
#endif
if (!p) p = "";
while (*p) {
if (*p != '%') {
/* if (str_l < str_m) str[str_l++] = *p++; -- this would be sufficient */
/* but the following code achieves better performance for cases
* where format string is long and contains few conversions */
const char *q = strchr(p+1,'%');
size_t n = !q ? strlen(p) : (q-p);
if (str_l < str_m) {
size_t avail = str_m-str_l;
fast_memcpy(str+str_l, p, (n>avail?avail:n));
}
p += n; str_l += n;
} else {
const char *starting_p;
size_t min_field_width = 0, precision = 0;
int zero_padding = 0, precision_specified = 0, justify_left = 0;
int alternate_form = 0, force_sign = 0;
int space_for_positive = 1; /* If both the ' ' and '+' flags appear,
the ' ' flag should be ignored. */
char length_modifier = '\0'; /* allowed values: \0, h, l, L */
char tmp[32];/* temporary buffer for simple numeric->string conversion */
 
const char *str_arg; /* string address in case of string argument */
size_t str_arg_l; /* natural field width of arg without padding
and sign */
unsigned char uchar_arg;
/* unsigned char argument value - only defined for c conversion.
N.B. standard explicitly states the char argument for
the c conversion is unsigned */
 
size_t number_of_zeros_to_pad = 0;
/* number of zeros to be inserted for numeric conversions
as required by the precision or minimal field width */
 
size_t zero_padding_insertion_ind = 0;
/* index into tmp where zero padding is to be inserted */
 
char fmt_spec = '\0';
/* current conversion specifier character */
 
str_arg = credits;/* just to make compiler happy (defined but not used)*/
str_arg = NULL;
starting_p = p; p++; /* skip '%' */
/* parse flags */
while (*p == '0' || *p == '-' || *p == '+' ||
*p == ' ' || *p == '#' || *p == '\'') {
switch (*p) {
case '0': zero_padding = 1; break;
case '-': justify_left = 1; break;
case '+': force_sign = 1; space_for_positive = 0; break;
case ' ': force_sign = 1;
/* If both the ' ' and '+' flags appear, the ' ' flag should be ignored */
#ifdef PERL_COMPATIBLE
/* ... but in Perl the last of ' ' and '+' applies */
space_for_positive = 1;
#endif
break;
case '#': alternate_form = 1; break;
case '\'': break;
}
p++;
}
/* If the '0' and '-' flags both appear, the '0' flag should be ignored. */
 
/* parse field width */
if (*p == '*') {
int j;
p++; j = va_arg(ap, int);
if (j >= 0) min_field_width = j;
else { min_field_width = -j; justify_left = 1; }
} else if (isdigit((int)(*p))) {
/* size_t could be wider than unsigned int;
make sure we treat argument like common implementations do */
unsigned int uj = *p++ - '0';
while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0');
min_field_width = uj;
}
/* parse precision */
if (*p == '.') {
p++; precision_specified = 1;
if (*p == '*') {
int j = va_arg(ap, int);
p++;
if (j >= 0) precision = j;
else {
precision_specified = 0; precision = 0;
/* NOTE:
* Solaris 2.6 man page claims that in this case the precision
* should be set to 0. Digital Unix 4.0, HPUX 10 and BSD man page
* claim that this case should be treated as unspecified precision,
* which is what we do here.
*/
}
} else if (isdigit((int)(*p))) {
/* size_t could be wider than unsigned int;
make sure we treat argument like common implementations do */
unsigned int uj = *p++ - '0';
while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0');
precision = uj;
}
}
/* parse 'h', 'l' and 'll' length modifiers */
if (*p == 'h' || *p == 'l') {
length_modifier = *p; p++;
if (length_modifier == 'l' && *p == 'l') { /* double l = long long */
#ifdef SNPRINTF_LONGLONG_SUPPORT
length_modifier = '2'; /* double l encoded as '2' */
#else
length_modifier = 'l'; /* treat it as a single 'l' */
#endif
p++;
}
}
fmt_spec = *p;
/* common synonyms: */
switch (fmt_spec) {
case 'i': fmt_spec = 'd'; break;
case 'D': fmt_spec = 'd'; length_modifier = 'l'; break;
case 'U': fmt_spec = 'u'; length_modifier = 'l'; break;
case 'O': fmt_spec = 'o'; length_modifier = 'l'; break;
default: break;
}
/* get parameter value, do initial processing */
switch (fmt_spec) {
case '%': /* % behaves similar to 's' regarding flags and field widths */
case 'c': /* c behaves similar to 's' regarding flags and field widths */
case 's':
length_modifier = '\0'; /* wint_t and wchar_t not supported */
/* the result of zero padding flag with non-numeric conversion specifier*/
/* is undefined. Solaris and HPUX 10 does zero padding in this case, */
/* Digital Unix and Linux does not. */
#if !defined(SOLARIS_COMPATIBLE) && !defined(HPUX_COMPATIBLE)
zero_padding = 0; /* turn zero padding off for string conversions */
#endif
str_arg_l = 1;
switch (fmt_spec) {
case '%':
str_arg = p; break;
case 'c': {
int j = va_arg(ap, int);
uchar_arg = (unsigned char) j; /* standard demands unsigned char */
str_arg = (const char *) &uchar_arg;
break;
}
case 's':
str_arg = va_arg(ap, const char *);
if (!str_arg) str_arg_l = 0;
/* make sure not to address string beyond the specified precision !!! */
else if (!precision_specified) str_arg_l = strlen(str_arg);
/* truncate string if necessary as requested by precision */
else if (precision == 0) str_arg_l = 0;
else {
/* memchr on HP does not like n > 2^31 !!! */
const char *q = memchr(str_arg, '\0',
precision <= 0x7fffffff ? precision : 0x7fffffff);
str_arg_l = !q ? precision : (q-str_arg);
}
break;
default: break;
}
break;
case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': {
/* NOTE: the u, o, x, X and p conversion specifiers imply
the value is unsigned; d implies a signed value */
 
int arg_sign = 0;
/* 0 if numeric argument is zero (or if pointer is NULL for 'p'),
+1 if greater than zero (or nonzero for unsigned arguments),
-1 if negative (unsigned argument is never negative) */
 
int int_arg = 0; unsigned int uint_arg = 0;
/* only defined for length modifier h, or for no length modifiers */
 
long int long_arg = 0; unsigned long int ulong_arg = 0;
/* only defined for length modifier l */
 
void *ptr_arg = NULL;
/* pointer argument value -only defined for p conversion */
 
#ifdef SNPRINTF_LONGLONG_SUPPORT
long long int long_long_arg = 0;
unsigned long long int ulong_long_arg = 0;
/* only defined for length modifier ll */
#endif
if (fmt_spec == 'p') {
/* HPUX 10: An l, h, ll or L before any other conversion character
* (other than d, i, u, o, x, or X) is ignored.
* Digital Unix:
* not specified, but seems to behave as HPUX does.
* Solaris: If an h, l, or L appears before any other conversion
* specifier (other than d, i, u, o, x, or X), the behavior
* is undefined. (Actually %hp converts only 16-bits of address
* and %llp treats address as 64-bit data which is incompatible
* with (void *) argument on a 32-bit system).
*/
#ifdef SOLARIS_COMPATIBLE
# ifdef SOLARIS_BUG_COMPATIBLE
/* keep length modifiers even if it represents 'll' */
# else
if (length_modifier == '2') length_modifier = '\0';
# endif
#else
length_modifier = '\0';
#endif
ptr_arg = va_arg(ap, void *);
if (ptr_arg != NULL) arg_sign = 1;
} else if (fmt_spec == 'd') { /* signed */
switch (length_modifier) {
case '\0':
case 'h':
/* It is non-portable to specify a second argument of char or short
* to va_arg, because arguments seen by the called function
* are not char or short. C converts char and short arguments
* to int before passing them to a function.
*/
int_arg = va_arg(ap, int);
if (int_arg > 0) arg_sign = 1;
else if (int_arg < 0) arg_sign = -1;
break;
case 'l':
long_arg = va_arg(ap, long int);
if (long_arg > 0) arg_sign = 1;
else if (long_arg < 0) arg_sign = -1;
break;
#ifdef SNPRINTF_LONGLONG_SUPPORT
case '2':
long_long_arg = va_arg(ap, long long int);
if (long_long_arg > 0) arg_sign = 1;
else if (long_long_arg < 0) arg_sign = -1;
break;
#endif
}
} else { /* unsigned */
switch (length_modifier) {
case '\0':
case 'h':
uint_arg = va_arg(ap, unsigned int);
if (uint_arg) arg_sign = 1;
break;
case 'l':
ulong_arg = va_arg(ap, unsigned long int);
if (ulong_arg) arg_sign = 1;
break;
#ifdef SNPRINTF_LONGLONG_SUPPORT
case '2':
ulong_long_arg = va_arg(ap, unsigned long long int);
if (ulong_long_arg) arg_sign = 1;
break;
#endif
}
}
str_arg = tmp; str_arg_l = 0;
/* NOTE:
* For d, i, u, o, x, and X conversions, if precision is specified,
* the '0' flag should be ignored. This is so with Solaris 2.6,
* Digital UNIX 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with Perl.
*/
#ifndef PERL_COMPATIBLE
if (precision_specified) zero_padding = 0;
#endif
if (fmt_spec == 'd') {
if (force_sign && arg_sign >= 0)
tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
/* leave negative numbers for sprintf to handle,
to avoid handling tricky cases like (short int)(-32768) */
#ifdef LINUX_COMPATIBLE
} else if (fmt_spec == 'p' && force_sign && arg_sign > 0) {
tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
#endif
} else if (alternate_form) {
if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X') )
{ tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = fmt_spec; }
/* alternate form should have no effect for p conversion, but ... */
#ifdef HPUX_COMPATIBLE
else if (fmt_spec == 'p'
/* HPUX 10: for an alternate form of p conversion,
* a nonzero result is prefixed by 0x. */
#ifndef HPUX_BUG_COMPATIBLE
/* Actually it uses 0x prefix even for a zero value. */
&& arg_sign != 0
#endif
) { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = 'x'; }
#endif
}
zero_padding_insertion_ind = str_arg_l;
if (!precision_specified) precision = 1; /* default precision is 1 */
if (precision == 0 && arg_sign == 0
#if defined(HPUX_BUG_COMPATIBLE) || defined(LINUX_COMPATIBLE)
&& fmt_spec != 'p'
/* HPUX 10 man page claims: With conversion character p the result of
* converting a zero value with a precision of zero is a null string.
* Actually HP returns all zeroes, and Linux returns "(nil)". */
#endif
) {
/* converted to null string */
/* When zero value is formatted with an explicit precision 0,
the resulting formatted string is empty (d, i, u, o, x, X, p). */
} else {
char f[5]; int f_l = 0;
f[f_l++] = '%'; /* construct a simple format string for sprintf */
if (!length_modifier) { }
else if (length_modifier=='2') { f[f_l++] = 'l'; f[f_l++] = 'l'; }
else f[f_l++] = length_modifier;
f[f_l++] = fmt_spec; f[f_l++] = '\0';
if (fmt_spec == 'p') str_arg_l += sprintf(tmp+str_arg_l, f, ptr_arg);
else if (fmt_spec == 'd') { /* signed */
switch (length_modifier) {
case '\0':
case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, int_arg); break;
case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, long_arg); break;
#ifdef SNPRINTF_LONGLONG_SUPPORT
case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,long_long_arg); break;
#endif
}
} else { /* unsigned */
switch (length_modifier) {
case '\0':
case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, uint_arg); break;
case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, ulong_arg); break;
#ifdef SNPRINTF_LONGLONG_SUPPORT
case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,ulong_long_arg);break;
#endif
}
}
/* include the optional minus sign and possible "0x"
in the region before the zero padding insertion point */
if (zero_padding_insertion_ind < str_arg_l &&
tmp[zero_padding_insertion_ind] == '-') {
zero_padding_insertion_ind++;
}
if (zero_padding_insertion_ind+1 < str_arg_l &&
tmp[zero_padding_insertion_ind] == '0' &&
(tmp[zero_padding_insertion_ind+1] == 'x' ||
tmp[zero_padding_insertion_ind+1] == 'X') ) {
zero_padding_insertion_ind += 2;
}
}
{ size_t num_of_digits = str_arg_l - zero_padding_insertion_ind;
if (alternate_form && fmt_spec == 'o'
#ifdef HPUX_COMPATIBLE /* ("%#.o",0) -> "" */
&& (str_arg_l > 0)
#endif
#ifdef DIGITAL_UNIX_BUG_COMPATIBLE /* ("%#o",0) -> "00" */
#else
/* unless zero is already the first character */
&& !(zero_padding_insertion_ind < str_arg_l
&& tmp[zero_padding_insertion_ind] == '0')
#endif
) { /* assure leading zero for alternate-form octal numbers */
if (!precision_specified || precision < num_of_digits+1) {
/* precision is increased to force the first character to be zero,
except if a zero value is formatted with an explicit precision
of zero */
precision = num_of_digits+1; precision_specified = 1;
}
}
/* zero padding to specified precision? */
if (num_of_digits < precision)
number_of_zeros_to_pad = precision - num_of_digits;
}
/* zero padding to specified minimal field width? */
if (!justify_left && zero_padding) {
int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
if (n > 0) number_of_zeros_to_pad += n;
}
break;
}
default: /* unrecognized conversion specifier, keep format string as-is*/
zero_padding = 0; /* turn zero padding off for non-numeric convers. */
#ifndef DIGITAL_UNIX_COMPATIBLE
justify_left = 1; min_field_width = 0; /* reset flags */
#endif
#if defined(PERL_COMPATIBLE) || defined(LINUX_COMPATIBLE)
/* keep the entire format string unchanged */
str_arg = starting_p; str_arg_l = p - starting_p;
/* well, not exactly so for Linux, which does something inbetween,
* and I don't feel an urge to imitate it: "%+++++hy" -> "%+y" */
#else
/* discard the unrecognized conversion, just keep *
* the unrecognized conversion character */
str_arg = p; str_arg_l = 0;
#endif
if (*p) str_arg_l++; /* include invalid conversion specifier unchanged
if not at end-of-string */
break;
}
if (*p) p++; /* step over the just processed conversion specifier */
/* insert padding to the left as requested by min_field_width;
this does not include the zero padding in case of numerical conversions*/
if (!justify_left) { /* left padding with blank or zero */
int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
if (n > 0) {
if (str_l < str_m) {
size_t avail = str_m-str_l;
fast_memset(str+str_l, (zero_padding?'0':' '), (n>avail?avail:n));
}
str_l += n;
}
}
/* zero padding as requested by the precision or by the minimal field width
* for numeric conversions required? */
if (number_of_zeros_to_pad <= 0) {
/* will not copy first part of numeric right now, *
* force it to be copied later in its entirety */
zero_padding_insertion_ind = 0;
} else {
/* insert first part of numerics (sign or '0x') before zero padding */
int n = zero_padding_insertion_ind;
if (n > 0) {
if (str_l < str_m) {
size_t avail = str_m-str_l;
fast_memcpy(str+str_l, str_arg, (n>avail?avail:n));
}
str_l += n;
}
/* insert zero padding as requested by the precision or min field width */
n = number_of_zeros_to_pad;
if (n > 0) {
if (str_l < str_m) {
size_t avail = str_m-str_l;
fast_memset(str+str_l, '0', (n>avail?avail:n));
}
str_l += n;
}
}
/* insert formatted string
* (or as-is conversion specifier for unknown conversions) */
{ int n = str_arg_l - zero_padding_insertion_ind;
if (n > 0) {
if (str_l < str_m) {
size_t avail = str_m-str_l;
fast_memcpy(str+str_l, str_arg+zero_padding_insertion_ind,
(n>avail?avail:n));
}
str_l += n;
}
}
/* insert right padding */
if (justify_left) { /* right blank padding to the field width */
int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
if (n > 0) {
if (str_l < str_m) {
size_t avail = str_m-str_l;
fast_memset(str+str_l, ' ', (n>avail?avail:n));
}
str_l += n;
}
}
}
}
#if defined(NEED_SNPRINTF_ONLY)
va_end(ap);
#endif
if (str_m > 0) { /* make sure the string is null-terminated
even at the expense of overwriting the last character
(shouldn't happen, but just in case) */
str[str_l <= str_m-1 ? str_l : str_m-1] = '\0';
}
/* Return the number of characters formatted (excluding trailing null
* character), that is, the number of characters that would have been
* written to the buffer if it were large enough.
*
* The value of str_l should be returned, but str_l is of unsigned type
* size_t, and snprintf is int, possibly leading to an undetected
* integer overflow, resulting in a negative return value, which is illegal.
* Both XSH5 and ISO C99 (at least the draft) are silent on this issue.
* Should errno be set to EOVERFLOW and EOF returned in this case???
*/
return (int) str_l;
}
#endif
/contrib/media/updf/pdf/snprintf.h
0,0 → 1,26
#ifndef _PORTABLE_SNPRINTF_H_
#define _PORTABLE_SNPRINTF_H_
 
#define PORTABLE_SNPRINTF_VERSION_MAJOR 2
#define PORTABLE_SNPRINTF_VERSION_MINOR 2
 
#ifdef HAVE_SNPRINTF
#include <stdio.h>
#else
extern int snprintf(char *, size_t, const char *, /*args*/ ...);
extern int vsnprintf(char *, size_t, const char *, va_list);
#endif
 
#if defined(HAVE_SNPRINTF) && defined(PREFER_PORTABLE_SNPRINTF)
extern int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...);
extern int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap);
#define snprintf portable_snprintf
#define vsnprintf portable_vsnprintf
#endif
 
extern int asprintf (char **ptr, const char *fmt, /*args*/ ...);
extern int vasprintf (char **ptr, const char *fmt, va_list ap);
extern int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...);
extern int vasnprintf(char **ptr, size_t str_m, const char *fmt, va_list ap);
 
#endif