/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 |