+Draw/, Report/, PdfDraw/, DocTypes/: minimalistic PDF/A support

+plugin/wincert: basic Win32 certificate manipulation and PDF digital signing

git-svn-id: svn://ultimatepp.org/upp/trunk@3715 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
rylek 2011-07-27 09:23:37 +00:00
parent 80abeb8192
commit 9ab49c4b9f
13 changed files with 777 additions and 29 deletions

View file

@ -111,7 +111,7 @@ public:
virtual ~DocReport();
};
String Pdf(const DocReport& report);
String Pdf(const DocReport& report, bool pdfa = false);
class ReportCtrl : public Ctrl {
public:

View file

@ -172,10 +172,10 @@ ReportDlg::ReportDlg()
pdf <<= THISBACK(Pdf);
}
String Pdf(const DocReport& report)
String Pdf(const DocReport& report, bool pdfa)
{
return GetDrawingToPdfFn() ? (*GetDrawingToPdfFn())(report.GetPages(), report.GetPageSize(),
200)
200, pdfa)
: String();
}

View file

@ -865,7 +865,7 @@ void DrawTLText(Draw& draw, int x, int y, int cx, const wchar *text, Font font =
Color ink = SColorText(), int accesskey = 0);
typedef String (*DrawingToPdfFnType)(const Array<Drawing>& report, Size pagesize, int margin);
typedef String (*DrawingToPdfFnType)(const Array<Drawing>& report, Size pagesize, int margin, bool pdfa);
void SetDrawingToPdfFn(DrawingToPdfFnType Pdf);
DrawingToPdfFnType GetDrawingToPdfFn();

View file

@ -8,6 +8,100 @@ NAMESPACE_UPP
#define PDF_COMPRESS
#define USE_TTF
static const char ICC_ColorSpaceInfo[] =
"H\211\234\226yTSw\026\307\177o\311\236\220\225\260\303c\r[\200\260\006\2205la\221"
"\035\004Q\bI\b\001\022BH\330\005AD\005\024ED\204\252\2252\326mtFOE\235.\256c\255\016"
"\326}\352\322\003\3650\352\3508\264\026\327\216\235\0278G\235Ng\246\323\357\037\357"
"\3679\367w\357\357\335\337\275\367\235\363\000\240'\245\252\265\3250\v\000\215\326"
"\240\317J\214\305\026\025\024b\244\t\000\003\n \002\021\0002y\255.-;!\a\340\222\306"
"K\260Z\334\t\374\213\236^\a\220i\275\"L\312\3000\360\377\211-\327\351\r\000@\0318"
"\a(\224\265r\234;q\256\2527\350L\366\031\234y\245\225&\206Q\023\353\361\004q\2664"
"\261j\236\275\347|\3469\332\304\n\215V\201\263)g\235B\2430\361i\234W\327\031\2258"
"#\2518w\325\251\225\3658_\305\331\245\312\250Q\343\374\334\024\253Q\312j\001@\351"
"&\273A)/\307\331\017g\272>'K\202\363\002\000\310t\325;\\\372\016\033\224\r\006\323"
"\245$\325\272F\275ZUn\300\334\345\036\230(4T\214%)\353\253\224\006\2030C&\257\224"
"\351\025\230\244Z\243\223i\033\001\230\277\363\2348\246\332bx\221\203E\241\301\301"
"B\177\037\321;\205\372\257\233\277P\246\336\316\323\223\314\271\236A\374\vom?\347"
"W=\n\200x\026\257\315\372\267\266\322-\000\214\257\004\300\362\346[\233\313\373\000"
"0\361\276\035\276\370\316}\370\246y)7\030ta\276\276\365\365\365>j\245\334\307T\320"
"7\372\237\016\277@\357\274\317\307t\334\233\362`q\3122\231\261\312\200\231\352&\257"
"\256\2526\352\261Z\235L\256\304\204?\035\342_\035\370\363yxg)\313\224z\245\026\217"
"\310\303\247L\255U\341\355\326*\324\006u\265\026Sk\377S\023\177e\330O4?\327\270\270"
"c\257\001\257\330\a\260.\362\000\362\267\v\000\345\322\000R\264\r\337\201\336\364"
"-\225\222\a2\3605\337\341\336\374\334\317\t\372\367S\341>\323\243V\255\232\213\223"
"d\345`r\243\276n~\317\364Y\002\002\240\002&\340\001+`\017\234\201;\020\002\177\020"
"\002\302A4\210\a\311 \035\344\200\002\260\024\310A9\320\000=\250\a-\240\035t\201\036"
"\260\036l\002\303`;\030\003\273\301~p\020\214\203\217\301\t\360Gp\036|\t\256\201["
"`\022L\203\207`\006<\005\257 \b\"A\f\210\vYA\016\220+\344\005\371Cb(\022\212\207R"
"\241,\250\000*\201T\220\0262B-\320\n\250\a\352\207\206\241\035\320n\350\367\320Q\350"
"\004t\016\272\004}\005MA\017\240\357\240\2270\002\323a\036l\a\273\301\276\260\030"
"\216\201S\340\034x\t\254\202k\340&\270\023^\a\017\301\243\360>\3700|\002>\017_\203"
"'\341\207\360,\002\020\032\302G\034\021!\"F$H:R\210\224!z\244\025\351F\006\221Qd?"
"r\f9\213\\A&\221G\310\v\224\210rQ\f\025\242\341h\022\232\213\312\321\032\264\025\355"
"E\207\321]\350a\3644z\005\235Bg\320\327\004\006\301\226\340E\b#H\t\213\b*B=\241\213"
"0H\330I\370\210p\206p\2150MxJ$\022\371D\0011\204\230D, V\020\233\211\275\304\255\304"
"\003\304\343\304K\304\273\304Y\022\211dE\362\"E\220\322I2\222\201\324E\332B\332G\372"
"\214t\2314MzN\246\221\035\310\376\344\004r!YK\356 \017\222\367\220?%_&\337#\277\242"
"\260(\256\2240J:EAi\244\364Q\306(\307(\027)\323\224WT6U@\215\240\346P+\250\355\324"
"!\352~\352\031\352m\352\023\032\215\346D\v\245e\322\324\264\345\264!\332\357h\237"
"\323\246h/\350\034\272']B/\242\033\351\353\350\037\322\217\323\277\242?a0\030n\214"
"hF!\303\300X\307\330\3158\305\370\232\361\334\214k\346c&5S\230\265\231\215\230\035"
"6\273l\366\230Ia\2722c\230K\231M\314A\346!\346E\346#\026\205\345\306\222\260d\254"
"V\326\b\353(\353\006k\226\315e\213\330\351l\r\273\227\275\207}\216}\237C\342\270q"
"\3429\nN'\347\003\316)\316].\302u\346J\270r\356\n\356\030\367\fw\232G\344\txR^\005"
"\257\207\367[\336\004o\306\234c\036h\236g\336`>b\376\211\371$\037\341\273\361\245"
"\374*~\037\377 \377:\377\245\205\235E\214\205\322b\215\305~\213\313\026\317,m,\243"
"-\225\226\335\226\a,\257Y\276\264\302\254\342\255*\2556X\215[\335\261F\255=\2553\255"
"\353\255\267Y\237\261~d\303\263\t\267\221\333t\333\034\264\271i\v\333z\332f\3316\333"
"~`{\301v\326\316\336.\321Ng\267\305\356\224\335#{\276}\264}\205\375\200\375\247\366"
"\017\034\270\016\221\016j\207\001\207\317\034\376\212\231c1X\0256\204\235\306f\034"
"m\035\223\034\215\216;\034'\034_9\t\234r\235:\234\0168\335q\246:\213\235\313\234\a"
"\234O:\317\2708\270\244\271\264\270\354u\271\351Jq\025\273\226\273nv=\353\372\314"
"M\340\226\357\266\312m\334\355\276\300R \0254\t\366\nn\2733\334\243\334k\334G\335"
"\257z\020=\304\036\225\036[=\276\364\204=\203<\313=G</z\301^\301^j\257\255^\227\274"
"\t\336\241\336Z\357Q\357\033B\2720FX'\334+\234\362\341\373\244\372t\370\214\373<\366"
"u\361-\364\335\340{\326\367\265_\220_\225\337\230\337-\021G\224,\352\020\035\023}"
"\347\357\351/\367\037\361\277\032\300\bH\bh\v8\022\360m\240W\2402p[\340\237\203\270"
"AiA\253\202N\006\375#8$X\037\274?\370A\210KHI\310{!7\304<q\206\270W\374y(!46\264-"
"\364\343\320\027a\301a\206\260\203a\177\017\027\206W\206\357\t\277\277@\260@\271`"
"l\301\335\b\247\bY\304\216\210\311H,\262$\362\375\310\311(\307(Y\324h\3247\321\316"
"\321\212\350\235\321\367b<b*b\366\305<\216\365\213\325\307~\024\373L\022&Y&9\036\207"
"\304%\306u\307M\304s\342s\343\207\343\277NpJP%\354M\230I\fJlN<\236DHJI\332\220tCj"
"'\225KwKg\222C\222\227%\237N\241\247d\247\f\247|\223\352\231\252O=\226\006\247%\247"
"mL\273\275\320u\241v\341x:H\227\246oL\277\223!\310\250\311\370C&13#s$\363/Y\242\254"
"\226\254\263\331\334\354\342\354=\331Osbs\372rn\345\272\347\032sO\3461\363\212\362"
"v\347=\313\217\313\357\317\237\\\344\273h\331\242\363\005\326\005\352\202#\205\244"
"\302\274\302\235\205\263\213\343\027oZ<]\024T\324Ut}\211`I\303\222sK\255\227V-\375"
"\244\230Y,+>TB(\311/\331S\362\203,]6*\233-\225\226\276W:#\227\3107\313\037*\242\025"
"\003\212\a\312\be\277\362^YDY\177\331}U\204j\243\352AyT\371`\371#\265D=\254\376\266"
"\"\251b{\305\263\312\364\312\017+\177\254\312\257:\240!kJ4G\265\034m\245\366t\265"
"}uC\365%\235\227\256K7Y\023V\263\251fF\237\242\337Y\v\325.\251=b\340\341?S\027\214"
"\356\306\225\306\251\272\310\272\221\272\347\365y\365\207\032\330\r\332\206\v\215"
"\236\215k\032\3575%4\375\246\031m\2267\237lqlio\231Z\026\263lG+\324Z\332z\262\315"
"\271\255\263mzy\342\362]\355\324\366\312\366?u\370u\364w|\277\"\177\305\261N\273\316"
"\345\235wW&\256\334\333e\326\245\357\272\261*|\325\366\325\350j\365\352\2115\001k"
"\266\254y\335\255\350\376\242\307\257g\260\347\207^y\357\027kEk\207\326\376\270\256"
"l\335D_p\337\266\365\304\365\332\365\3277Dm\330\325\317\356o\352\277\2731m\343\341"
"\001l\240{\340\373M\305\233\316\r\006\016n\337L\335l\334<9\224\372O\000\244\001[\376"
"\230\270\231$\231\220\231\374\232h\232\325\233B\233\257\234\034\234\211\234\367\235"
"d\235\322\236@\236\256\237\035\237\213\237\372\240i\240\330\241G\241\266\242&\242"
"\226\243\006\243v\243\346\244V\244\307\2458\245\251\246\032\246\213\246\375\247n\247"
"\340\250R\250\304\2517\251\251\252\034\252\217\253\002\253u\253\351\254\\\254\320"
"\255D\255\270\256-\256\241\257\026\257\213\260\000\260u\260\352\261`\261\326\262K"
"\262\302\2638\263\256\264%\264\234\265\023\265\212\266\001\266y\266\360\267h\267\340"
"\270Y\270\321\271J\271\302\272;\272\265\273.\273\247\274!\274\233\275\025\275\217"
"\276\n\276\204\276\377\277z\277\365\300p\300\354\301g\301\343\302_\302\333\303X\303"
"\324\304Q\304\316\305K\305\310\306F\306\303\307A\307\277\310=\310\274\311:\311\271"
"\3128\312\267\3136\313\266\3145\314\265\3155\315\265\3166\316\266\3177\317\270\320"
"9\320\272\321<\321\276\322?\322\301\323D\323\306\324I\324\313\325N\325\321\326U\326"
"\330\327\\\327\340\330d\330\350\331l\331\361\332v\332\373\333\200\334\005\334\212"
"\335\020\335\226\336\034\336\242\337)\337\257\3406\340\275\341D\341\314\342S\342\333"
"\343c\343\353\344s\344\374\345\204\346\r\346\226\347\037\347\251\3502\350\274\351"
"F\351\320\352[\352\345\353p\353\373\354\206\355\021\355\234\356(\356\264\357@\357"
"\314\360X\360\345\361r\361\377\362\214\363\031\363\247\3644\364\302\365P\365\336\366"
"m\366\373\367\212\370\031\370\250\3718\371\307\372W\372\347\373w\374\a\374\230\375"
")\375\272\376K\376\334\377m\377\377\002\f\000\367\204\363\373";
dword PdfDraw::GetInfo() const
{
return DOTS;
@ -18,10 +112,11 @@ Size PdfDraw::GetPageSize() const
return pgsz;
}
void PdfDraw::Init(int pagecx, int pagecy, int _margin)
void PdfDraw::Init(int pagecx, int pagecy, int _margin, bool _pdfa)
{
Clear();
margin = _margin;
pdfa = _pdfa;
pgsz.cx = pagecx;
pgsz.cy = pagecy;
pgsz += margin;
@ -55,20 +150,22 @@ void PdfDraw::PutRect(const Rect& rc)
<< Pt(rc.Width()) << ' ' << Pt(rc.Height()) << " re\n";
}
int PdfDraw::PutStream(const String& data, const String& keys)
int PdfDraw::PutStream(const String& data, const String& keys, bool compress)
{
#ifdef PDF_COMPRESS
String c = ZCompress(data);
if(c.GetLength() < data.GetLength()) {
BeginObj();
out << "<< " << keys
<< "/Length " << c.GetLength() << " "
<< "/Length1 " << data.GetLength() << " "
<< "/Filter /FlateDecode "
<< " >>\n"
<< "stream\r\n" << c << "\r\nendstream\n";
EndObj();
return offset.GetCount();
if(compress) {
String c = ZCompress(data);
if(c.GetLength() < data.GetLength()) {
BeginObj();
out << "<< " << keys
<< "/Length " << c.GetLength() << " "
<< "/Length1 " << data.GetLength() << " "
<< "/Filter /FlateDecode "
<< " >>\n"
<< "stream\r\n" << c << "\r\nendstream\n";
EndObj();
return offset.GetCount();
}
}
#endif
BeginObj();
@ -669,7 +766,7 @@ String PdfDraw::Finish()
}
}
imgobj << "/Type /XObject /Subtype /Image" << wh
<< " /BitsPerComponent 8 /ColorSpace /DeviceRGB";
<< " /BitsPerComponent 8 /ColorSpace /DeviceRGB /Intent /Perceptual";
}
}
if(mask >= 0)
@ -965,6 +1062,21 @@ String PdfDraw::Finish()
out << ">>\n";
EndObj();
int dflt_rgb_def = -1;
if(pdfa) {
int icc_info = BeginObj();
static const int data_len = __countof(ICC_ColorSpaceInfo) - 1;
out << "<</Length " << data_len << "/Filter/FlateDecode/N 3>>\n"
"stream\n";
out.Cat(ICC_ColorSpaceInfo, data_len);
out << "\nendstream\n";
EndObj();
dflt_rgb_def = BeginObj();
out << "[/ICCBased " << icc_info << " 0 R]\n";
EndObj();
}
int resources = BeginObj();
out << "<< /Font " << fonts << " 0 R\n"
"/ProcSet [ /PDF /Text /ImageB /ImageC ]\n";
@ -982,6 +1094,9 @@ String PdfDraw::Finish()
out << "/Pat" << (i + 1) << ' ' << patternobj[i] << " 0 R ";
out << ">>\n";
}
if(pdfa) {
out << "/ColorSpace <</DefaultRGB " << dflt_rgb_def << " 0 R>>\n";
}
out << ">>\n";
EndObj();
@ -1009,11 +1124,66 @@ String PdfDraw::Finish()
"/Count 0\n"
">>\n";
EndObj();
int pdfa_metadata = -1;
if(pdfa) {
StringBuffer metadata;
metadata <<
"<?xpacket id=\"" << Uuid::Create() << "\"?>\n"
"<x:xmpmeta xmlns:x=\"adobe:ns:meta/\" x:xmptk=\"PDFNet\">\n"
"<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n"
"<rdf:Description rdf:about=\"\"\n"
"xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">\n"
"<xmp:CreateDate>0000-01-01</xmp:CreateDate>\n"
"<xmp:ModifyDate>0000-01-01</xmp:ModifyDate>\n"
"<xmp:CreatorTool/>\n"
"</rdf:Description>\n"
"<rdf:Description rdf:about=\"\"\n"
"xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n"
"<dc:title>\n"
"<rdf:Alt>\n"
"<rdf:li xml:lang=\"x-default\"/>\n"
"</rdf:Alt>\n"
"</dc:title>\n"
"<dc:creator>\n"
"<rdf:Seq>\n"
"<rdf:li/>\n"
"</rdf:Seq>\n"
"</dc:creator>\n"
"<dc:description>\n"
"<rdf:Alt>\n"
"<rdf:li xml:lang=\"x-default\"/>\n"
"</rdf:Alt>\n"
"</dc:description>\n"
"</rdf:Description>\n"
"<rdf:Description rdf:about=\"\"\n"
"xmlns:pdf=\"http://ns.adobe.com/pdf/1.3/\">\n"
"<pdf:Keywords/>\n"
"<pdf:Producer/>\n"
"</rdf:Description>\n"
"<rdf:Description rdf:about=\"\"\n"
"xmlns:pdfaid=\"http://www.aiim.org/pdfa/ns/id/\">\n"
"<pdfaid:part>1</pdfaid:part>\n"
"<pdfaid:conformance>B</pdfaid:conformance>\n"
"</rdf:Description>\n"
"</rdf:RDF>\n"
"</x:xmpmeta>\n";
StringBuffer meta_head;
meta_head << "/Type/Metadata/Subtype/XML";
pdfa_metadata = PutStream(metadata, meta_head, false);
}
int catalog = BeginObj();
out << "<< /Type /Catalog\n"
<< "/Outlines " << outlines << " 0 R\n"
<< "/Pages " << pages << " 0 R\n"
<< ">>\n";
<< "/Pages " << pages << " 0 R\n";
if(pdfa_metadata >= 0) {
out << "/Metadata " << pdfa_metadata << " 0 R\n";
}
out << ">>\n";
EndObj();
int startxref = out.GetCount();
out << "xref\n"

View file

@ -284,13 +284,14 @@ private:
double linewidth;
int margin;
uint64 patternid;
bool pdfa;
inline double Pt(double dot) { return 0.12 * dot; }
int Pos() { return offset.GetCount() + 1; }
int BeginObj();
void EndObj();
int PutStream(const String& data, const String& keys = Null);
int PutStream(const String& data, const String& keys = Null, bool compress = true);
void PutRect(const Rect& rc);
void PutrgColor(Color rg, uint64 pattern = 0);
@ -320,7 +321,7 @@ private:
M22() : a(1), b(0), c(0), d(1) {}
};
void Init(int pagecx, int pagecy, int margin);
void Init(int pagecx, int pagecy, int margin, bool pdfa);
struct RGlyph : Moveable<RGlyph> {
String data;
@ -334,8 +335,8 @@ public:
String Finish();
void Clear();
PdfDraw(int pagecx, int pagecy) { Init(pagecx, pagecy, 0); }
PdfDraw(Size pgsz = Size(5100, 6600)) { Init(pgsz.cx, pgsz.cy, 0); }
PdfDraw(int pagecx, int pagecy, bool pdfa = false) { Init(pagecx, pagecy, 0, pdfa); }
PdfDraw(Size pgsz = Size(5100, 6600), bool pdfa = false) { Init(pgsz.cx, pgsz.cy, 0, pdfa); }
};
String Pdf(const Array<Drawing>& report, Size pagesize, int margin);

View file

@ -2,9 +2,9 @@
NAMESPACE_UPP
String Pdf(const Array<Drawing>& report, Size sz, int margin)
String Pdf(const Array<Drawing>& report, Size sz, int margin, bool pdfa)
{
PdfDraw w(sz + 2 * margin);
PdfDraw w(sz + 2 * margin, pdfa);
for(int i = 0; i < report.GetCount(); i++) {
w.StartPage();
w.DrawDrawing(margin, margin, sz.cx, sz.cy, report[i]);

View file

@ -141,7 +141,7 @@ public:
ReportWindow();
};
String Pdf(Report& report);
String Pdf(Report& report, bool pdfa = false);
void Print(Report& r, PrinterJob& pd, bool center = true);
bool DefaultPrint(Report& r, int i, const char *_name = t_("Report"));
bool Print(Report& r, int i, const char *name = t_("Report"));

View file

@ -214,10 +214,10 @@ ReportWindow::ReportWindow()
pdf <<= THISBACK(Pdf);
}
String Pdf(Report& report)
String Pdf(Report& report, bool pdfa)
{
return GetDrawingToPdfFn() && report.GetCount() ?
(*GetDrawingToPdfFn())(report.GetPages(), report.GetPage(0).GetSize(), 200)
(*GetDrawingToPdfFn())(report.GetPages(), report.GetPage(0).GetSize(), 200, pdfa)
: String();
}

View file

@ -0,0 +1,4 @@
#ifndef _plugin_wincert_icpp_init_stub
#define _plugin_wincert_icpp_init_stub
#include "Web/init"
#endif

View file

@ -0,0 +1,363 @@
#include "wincert.h"
class PDFInfo {
public:
struct Reference : Moveable<Reference> {
Reference(const Nuller& = Null) : index(0) {}
Reference(int index_, word revision_) : index(index_), revision(revision_) {}
String ToString() const;
bool IsNullInstance() const { return index == 0; }
int index;
word revision;
};
PDFInfo();
void Clear();
bool Open(const String& pdf_data);
public:
bool SkipWhite();
bool Check(const char *text);
int ScanInteger();
Reference ScanReference();
bool ParseXRef(int startxref);
bool ParseRoot();
bool ParsePageTree(Reference root);
bool Seek(Reference ref);
public:
enum { MAX_PDF_OBJECTS = 1000000 };
String pdf_data;
Vector<int> objects;
Vector<word> revisions;
Vector<Reference> pages;
int startxref;
Reference trailer_root;
Reference trailer_info;
Reference page_tree;
String root_object;
const char *term;
};
String PDFInfo::Reference::ToString() const
{
StringBuffer out;
out << index << ' ' << (int)revision << " R";
return out;
}
PDFInfo::PDFInfo()
{
Clear();
}
void PDFInfo::Clear()
{
pdf_data = Null;
objects.Clear();
revisions.Clear();
startxref = -1;
trailer_root = Null;
trailer_info = Null;
page_tree = Null;
root_object = Null;
term = NULL;
}
bool PDFInfo::Open(const String& pdf_data_)
{
Clear();
pdf_data = pdf_data_;
const char *begin = pdf_data.Begin();
term = pdf_data.End();
while(term > begin && (byte)term[-1] <= ' ')
term--;
if(term - begin <= 5 || memcmp(term - 5, "%%EOF", 5))
return false;
term -= 5;
while(term > begin && !IsDigit(*--term))
;
while(term > begin && IsDigit(term[-1]))
term--;
startxref = ScanInt(term);
if(!ParseXRef(startxref))
return false;
if(!ParseRoot() || !ParsePageTree(page_tree))
return false;
return true;
}
bool PDFInfo::ParseXRef(int start)
{
if(start < 0 || start >= pdf_data.GetLength() - 20)
return false;
term = pdf_data.GetIter(start);
if(!Check("xref"))
return false;
while(!Check("trailer")) {
if(!SkipWhite())
return false;
int start = ScanInteger();
int count = ScanInteger();
if(start < 0 || count <= 0 || start + count > MAX_PDF_OBJECTS || !SkipWhite())
return false;
int end = start + count;
if(objects.GetCount() < end) {
objects.SetCountR(end, -1);
revisions.SetCountR(end, 0xffffu);
}
for(int i = 0; i < count; i++) {
int offset = ScanInteger();
int rev = ScanInteger();
if(!SkipWhite())
return false;
if(*term != 'f' && *term != 'n')
return false;
if(*term++ == 'f') {
offset = 0;
rev = 0xffffu;
}
if(objects[start + i] == -1) {
objects[start + i] = offset;
revisions[start + i] = rev;
}
}
}
if(!Check("<<"))
return false;
while(!Check(">>")) {
if(!SkipWhite())
return false;
if(Check("/Size")) {
int sz = ScanInteger();
if(sz < objects.GetCount())
return false;
objects.SetCount(sz, -1);
revisions.SetCount(sz, 0xffffu);
}
else if(Check("/Root")) {
Reference root = ScanReference();
if(IsNull(trailer_root))
trailer_root = root;
}
else if(Check("/Info")) {
Reference info = ScanReference();
if(IsNull(trailer_info))
trailer_info = info;
}
else if(Check("/Prev")) {
int prev = ScanInteger();
const char *store_term = term;
if(!ParseXRef(prev))
return false;
term = store_term;
}
else {
while(*++term && *term != '/' && !(*term == '>' && term[1] == '>'))
;
}
}
return true;
}
bool PDFInfo::Seek(Reference r)
{
if(r.index <= 0 || r.index >= objects.GetCount() || objects[r.index] == -1)
return false;
term = pdf_data.GetIter(objects[r.index]);
int index = ScanInteger();
if(index != r.index)
return false;
int rev = ScanInteger();
if(rev != r.revision)
return false;
if(!Check("obj") || !Check("<<"))
return false;
return true;
}
bool PDFInfo::ParseRoot()
{
if(!Seek(trailer_root))
return false;
const char *begin = term;
while(*term) {
if(Check("/Pages")) {
page_tree = ScanReference();
}
else
if(*term == '>' && term[1] == '>')
break;
else
term++;
}
root_object = String(begin, term);
return true;
}
bool PDFInfo::ParsePageTree(Reference node)
{
if(!Seek(node))
return false;
if(!Check("/Type"))
return false;
if(Check("/Pages")) {
if(!Check("/Kids") || !Check("["))
return false;
while(*term && !Check("]")) {
Reference kid = ScanReference();
const char *stack = term;
if(!ParsePageTree(kid))
return false;
term = stack;
}
return true;
}
else if(Check("/Page")) {
pages.Add(node);
return true;
}
else
return false;
}
bool PDFInfo::SkipWhite()
{
while(*term && (byte)*term <= ' ')
term++;
return !!*term;
}
bool PDFInfo::Check(const char *text)
{
if(!SkipWhite())
return false;
int l = strlen(text);
if(memcmp(term, text, l))
return false;
term += l;
return true;
}
int PDFInfo::ScanInteger()
{
if(!SkipWhite())
return Null;
return ScanInt(term, &term);
}
PDFInfo::Reference PDFInfo::ScanReference()
{
int obj = ScanInteger();
if(IsNull(obj))
return Reference();
int rev = ScanInteger();
if(IsNull(rev))
return Reference();
if(SkipWhite() && *term == 'R') {
term++;
return Reference(obj, rev);
}
return Reference();
}
String CertSignPDF(PCCERT_CONTEXT cert_context, const String& pdf_data)
{
PDFInfo pdfinfo;
if(!pdfinfo.Open(pdf_data) || pdfinfo.pages.IsEmpty())
return String::GetVoid();
StringBuffer out;
out.Cat(pdf_data);
int obj_index = pdfinfo.objects.GetCount();
int sign_object = obj_index++;
int sign_dict = obj_index++;
int new_root = obj_index++;
int sign_off = out.GetLength();
out << sign_object << " 0 obj\n"
"<</FT/Sig/T(Signature1)"
"/V " << sign_dict << " 0 R"
"/F 132/Type/Annot/Subtype/Widget/Rect[0 0 0 0]";
if(!IsNull(pdfinfo.page_tree))
out << "/P " << pdfinfo.pages[0];
out << ">>\n"
"endobj\n";
int dict_off = out.GetLength();
Time tm = GetSysTime();
out << sign_dict << " 0 obj\n"
"<</Type/Sig/Filter/Adobe.PPKLite/SubFilter/adbe.pkcs7.detached"
"/Reason()"
"/Location()"
"/ContactInfo()"
"/M(D:" << NFormat("%04d%02d%02d%02d%02d%02d+02'00'",
tm.year, tm.month, tm.day, tm.hour, tm.minute, tm.second)
<< ")/ByteRange [0 ";
int br_offset = out.GetLength();
int cert_length = 4096;
String tail = "/Contents <";
static const int RCL_CONT = 60;
for(;;) {
out.SetLength(br_offset);
out.Cat(' ', RCL_CONT);
out.Cat(tail);
int cert_offset = out.GetLength() - 1;
out.Cat('0', cert_length);
int post_offset = out.GetLength() + 1;
out << ">>>\n"
"endobj\n";
int root_off = out.GetLength();
out
<< new_root << " 0 obj\n"
"<<" << pdfinfo.root_object
<< "/AcroForm<</Fields[" << sign_object << " 0 R]/SigFlags 3>>>>\n"
"endobj\n";
int xref_offset = out.GetLength();
out << "xref\n"
<< sign_object << " 3\n"
<< FormatIntDec(sign_off, 10, '0') << " 00000 n\r\n"
<< FormatIntDec(dict_off, 10, '0') << " 00000 n\r\n"
<< FormatIntDec(root_off, 10, '0') << " 00000 n\r\n"
"trailer\n"
"<</Size " << obj_index << "/Root " << new_root << " 0 R";
if(!IsNull(pdfinfo.trailer_info))
out << "/Info " << pdfinfo.trailer_info;
out <<
"/ID [ <" << Uuid::Create() << "> <" << Uuid::Create() << "> ]\n"
"/Prev " << pdfinfo.startxref
<< ">>\n"
"startxref\n"
<< xref_offset
<< "\n"
"%%EOF\n";
int end_offset = out.GetLength();
String rcl_fmt = NFormat("%d %d %d ]", cert_offset,
post_offset, end_offset - post_offset);
ASSERT(rcl_fmt.GetLength() <= RCL_CONT);
memcpy(out.Begin() + br_offset, rcl_fmt, rcl_fmt.GetLength());
Vector<const BYTE *> sections;
Vector<DWORD> sizes;
sections.SetCount(2);
sizes.SetCount(2);
sections[0] = (const BYTE *)out.Begin();
sizes[0] = cert_offset;
sections[1] = (const BYTE *)out.Begin() + post_offset;
sizes[1] = end_offset - post_offset;
int sig_len = 2 * CertSignLength(cert_context, sections, sizes);
if(sig_len <= cert_length) {
String signature = BinHexEncode(CertSign(cert_context, sections, sizes));
ASSERT(signature.GetLength() <= cert_length);
memcpy(out.Begin() + cert_offset + 1, signature, signature.GetLength());
return out;
}
cert_length = sig_len;
}
}

View file

@ -0,0 +1,163 @@
#include "wincert.h"
String CertGetProperty(PCCERT_CONTEXT cert_context, DWORD propertyID)
{
dword len = 0;
if(!CertGetCertificateContextProperty(cert_context, propertyID, NULL, &len))
return String::GetVoid();
Buffer<byte> prop(len);
if(!CertGetCertificateContextProperty(cert_context, propertyID, prop, &len))
return String::GetVoid();
return String(~prop, len);
}
String CertGetName(PCCERT_CONTEXT cert_context, DWORD dwType, DWORD flags)
{
dword len = CertGetNameString(cert_context, dwType, flags, NULL, NULL, NULL);
if(!len)
return String::GetVoid();
Buffer<char> name(len);
if(!(len = CertGetNameString(cert_context, dwType, flags, NULL, name, len)))
return String::GetVoid();
return String(name, len - 1);
}
String CertGetNameAttr(PCCERT_CONTEXT cert_context, const char *attrOID)
{
dword len = CertGetNameString(cert_context, CERT_NAME_ATTR_TYPE, 0, (void *)attrOID, NULL, NULL);
if(!len)
return String::GetVoid();
Buffer<char> name(len);
if(!(len = CertGetNameString(cert_context, CERT_NAME_ATTR_TYPE, 0, (void *)attrOID, name, len)))
return String::GetVoid();
return String(name, len - 1);
}
/*
String CertGetHash(PCCERT_CONTEXT cert_context)
{
dword hash_len = 0;
CertGetCertificateContextProperty(cert_context, CERT_HASH_PROP_ID, NULL, &hash_len);
Buffer<byte> hash(hash_len);
CertGetCertificateContextProperty(cert_context, CERT_HASH_PROP_ID, hash, &hash_len);
String cert_hash(~hash, hash_len);
return cert_hash;
}
*/
/*
String CertGetName(PCCERT_CONTEXT cert_context)
{
dword type;
dword len = CertGetNameString(cert_context, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, &type, NULL, NULL);
Buffer<char> name(len);
CertGetNameString(cert_context, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, &type, name, len);
String cert_name(name, len ? len - 1 : 0);
return cert_name;
}
*/
CertInfo CertGetInfo(PCCERT_CONTEXT cert_context)
{
CertInfo out;
out.hash = CertGetProperty(cert_context, CERT_HASH_PROP_ID);
out.common_name = CertGetNameAttr(cert_context, szOID_COMMON_NAME);
out.organization = CertGetNameAttr(cert_context, szOID_ORGANIZATION_NAME);
out.organizational_unit = CertGetNameAttr(cert_context, szOID_ORGANIZATIONAL_UNIT_NAME);
out.email = CertGetNameAttr(cert_context, szOID_RSA_emailAddr);
out.issuer = CertGetName(cert_context, CERT_NAME_FRIENDLY_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG);
out.valid_from = Time(cert_context->pCertInfo->NotBefore);
out.valid_until = Time(cert_context->pCertInfo->NotAfter);
// out.valid_until = Cert
return out;
}
static void _sCertSignSet(PCCERT_CONTEXT cert_context, CRYPT_SIGN_MESSAGE_PARA& sign)
{
Zero(sign);
sign.cbSize = sizeof(sign);
sign.dwMsgEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
sign.pSigningCert = cert_context;
sign.HashAlgorithm.pszObjId = szOID_RSA_SHA1RSA;
sign.HashAlgorithm.Parameters.cbData = NULL;
sign.cMsgCert = 1;
sign.rgpMsgCert = &sign.pSigningCert;
sign.cAuthAttr = 0;
sign.dwInnerContentType = 0;
sign.cMsgCrl = 0;
sign.cUnauthAttr = 0;
sign.dwFlags = 0;
sign.pvHashAuxInfo = NULL;
sign.rgAuthAttr = NULL;
}
int CertSignLength(PCCERT_CONTEXT cert_context, Vector<const BYTE *>& sections, Vector<DWORD>& sizes)
{
ASSERT(sections.GetCount() == sizes.GetCount());
CRYPT_SIGN_MESSAGE_PARA sign;
_sCertSignSet(cert_context, sign);
const BYTE **psect = sections.Begin();
DWORD *psize = sizes.Begin();
DWORD out_size = 0;
if(!CryptSignMessage(&sign, TRUE, sections.GetCount(), psect, psize, NULL, &out_size))
return 0;
return out_size;
}
String CertSign(PCCERT_CONTEXT cert_context, Vector<const BYTE *>& sections, Vector<DWORD>& sizes)
{
ASSERT(sections.GetCount() == sizes.GetCount());
CRYPT_SIGN_MESSAGE_PARA sign;
_sCertSignSet(cert_context, sign);
const BYTE **psect = sections.Begin();
DWORD *psize = sizes.Begin();
DWORD out_size = 0;
if(!CryptSignMessage(&sign, TRUE, sections.GetCount(), psect, psize, NULL, &out_size))
return String::GetVoid();
Buffer<byte> out_data(out_size);
if(!CryptSignMessage(&sign, TRUE, sections.GetCount(), psect, psize, out_data, &out_size))
return String::GetVoid();
return String(~out_data, out_size);
}
String CertSign(PCCERT_CONTEXT context, const String& message)
{
Vector<const BYTE *> sections;
Vector<DWORD> sizes;
sections.SetCount(1, (const BYTE *)message.Begin());
sizes.SetCount(1, message.GetLength());
return CertSign(context, sections, sizes);
}
Array<CertInfo> CertEnum(HCERTSTORE store)
{
Array<CertInfo> out_list;
RLOG("store = " << FormatIntHex(store));
PCCERT_CONTEXT cert_context = NULL;
while(cert_context = CertEnumCertificatesInStore(store, cert_context)) {
out_list.Add() = CertGetInfo(cert_context);
}
return out_list;
}
PCCERT_CONTEXT CertFind(HCERTSTORE store, const String& hash)
{
CRYPT_HASH_BLOB hash_blob;
hash_blob.pbData = (BYTE *)hash.Begin();
hash_blob.cbData = hash.GetLength();
return CertFindCertificateInStore(store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
0, CERT_FIND_HASH, &hash_blob, NULL);
}

View file

@ -0,0 +1,34 @@
#ifndef _wincert_wincert_h
#define _wincert_wincert_h
#include <Web/Web.h>
#include <WinCrypt.h>
using namespace Upp;
struct CertInfo {
String hash;
String common_name;
String organization;
String organizational_unit;
String email;
String issuer;
Time valid_from;
Time valid_until;
};
String CertGetProperty(PCCERT_CONTEXT cert_context, DWORD propertyID);
String CertGetName(PCCERT_CONTEXT cert_context, DWORD dwType, DWORD flags = 0);
String CertGetNameAttr(PCCERT_CONTEXT cert_context, const char *attrOID);
CertInfo CertGetInfo(PCCERT_CONTEXT cert_context);
Array<CertInfo> CertEnum(HCERTSTORE store);
PCCERT_CONTEXT CertFind(HCERTSTORE store, const String& hash);
int CertSignLength(PCCERT_CONTEXT cert_context, Vector<const BYTE *>& sections, Vector<DWORD>& sizes);
String CertSign(PCCERT_CONTEXT cert_context, Vector<const BYTE *>& sections, Vector<DWORD>& sizes);
String CertSign(PCCERT_CONTEXT context, const String& message);
String CertSignPDF(PCCERT_CONTEXT cert_context, const String& pdf_data);
#endif

View file

@ -0,0 +1,13 @@
description "Minimum Win32 certificate support and PDF digital signing\377";
uses
Web;
library
Crypt32.lib;
file
wincert.h,
util.cpp,
pdf.cpp;