pdfengine.cc.svn-base

来自「SumatraPDF是一款小型开源的pdf阅读工具。虽然玲珑小巧(只有800多K」· SVN-BASE 代码 · 共 536 行

SVN-BASE
536
字号
/* Copyright Krzysztof Kowalczyk 2006-2007   License: GPLv2 */#include "base_util.h"#include "PdfEngine.h"#include "str_util.h"#include "utf_util.h"// in SumatraPDF.cppextern "C" char *GetPasswordForFile(WindowInfo *win, const WCHAR *fileName);/* hack to make fz_throw work in C++ */#ifdef nil#undef nil#define nil ((fz_error*)0)#endifstatic fz_error *pdf_getpageinfo(pdf_xref *xref, fz_obj *dict, fz_rect *bboxp, int *rotatep){    fz_rect bbox;    int rotate;    fz_obj *obj;    fz_error *error;    obj = fz_dictgets(dict, "CropBox");    if (!obj)        obj = fz_dictgets(dict, "MediaBox");    if (fz_isindirect(obj)) {        fz_obj* obj2;        error = pdf_loadindirect(&obj2, xref, obj);        if (error)            return error;        obj = obj2;    }    if (!fz_isarray(obj))        return fz_throw("syntaxerror: Page missing MediaBox");    bbox = pdf_torect(obj);    //pdf_logpage("bbox [%g %g %g %g]\n", bbox.x0, bbox.y0, bbox.x1, bbox.y1);    obj = fz_dictgets(dict, "Rotate");    rotate = 0;    if (fz_isint(obj))        rotate = fz_toint(obj);    //pdf_logpage("rotate %d\n", rotate);    if (bboxp)        *bboxp = bbox;    if (rotatep)        *rotatep = rotate;    return nil;}static HBITMAP createDIBitmapCommon(RenderedBitmap *bmp, HDC hdc){    int bmpDx = bmp->dx();    int bmpDy = bmp->dy();    int bmpRowSize = bmp->rowSize();    BITMAPINFOHEADER bmih;    bmih.biSize = sizeof(bmih);    bmih.biHeight = -bmpDy;    bmih.biWidth = bmpDx;    bmih.biPlanes = 1;    bmih.biBitCount = 24;    bmih.biCompression = BI_RGB;    bmih.biSizeImage = bmpDy * bmpRowSize;;    bmih.biXPelsPerMeter = bmih.biYPelsPerMeter = 0;    bmih.biClrUsed = bmih.biClrImportant = 0;    unsigned char* bmpData = bmp->data();    HBITMAP hbmp = ::CreateDIBitmap(hdc, &bmih, CBM_INIT, bmpData, (BITMAPINFO *)&bmih , DIB_RGB_COLORS);    return hbmp;}static void stretchDIBitsCommon(RenderedBitmap *bmp, HDC hdc, int leftMargin, int topMargin, int pageDx, int pageDy){    int bmpDx = bmp->dx();    int bmpDy = bmp->dy();    int bmpRowSize = bmp->rowSize();    BITMAPINFOHEADER bmih;    bmih.biSize = sizeof(bmih);    bmih.biHeight = -bmpDy;    bmih.biWidth = bmpDx;    bmih.biPlanes = 1;    // we could create this dibsection in monochrome    // if the printer is monochrome, to reduce memory consumption    // but splash is currently setup to return a full colour bitmap    bmih.biBitCount = 24;    bmih.biCompression = BI_RGB;    bmih.biSizeImage = bmpDy * bmpRowSize;;    bmih.biXPelsPerMeter = bmih.biYPelsPerMeter = 0;    bmih.biClrUsed = bmih.biClrImportant = 0;    unsigned char* bmpData = bmp->data();    ::StretchDIBits(hdc,        // destination rectangle        leftMargin, topMargin, pageDx, pageDy,        // source rectangle        0, 0, bmpDx, bmpDy,        bmpData,        (BITMAPINFO *)&bmih ,        DIB_RGB_COLORS,        SRCCOPY);}RenderedBitmap::RenderedBitmap(fz_pixmap *bitmap){    _bitmap = bitmap;}RenderedBitmap::~RenderedBitmap(){    if (_bitmap)        fz_droppixmap(_bitmap);}int RenderedBitmap::rowSize(){    int rowSize = ((_bitmap->w * 3 + 3) / 4) * 4;    return rowSize;}unsigned char *RenderedBitmap::data(){    unsigned char* bmpData = _bitmap->samples;    return bmpData;}HBITMAP RenderedBitmap::createDIBitmap(HDC hdc){    return createDIBitmapCommon(this, hdc);}void RenderedBitmap::stretchDIBits(HDC hdc, int leftMargin, int topMargin, int pageDx, int pageDy){    stretchDIBitsCommon(this, hdc, leftMargin, topMargin, pageDx, pageDy);}fz_matrix PdfEngine::viewctm(pdf_page *page, float zoom, int rotate){    fz_matrix ctm;    ctm = fz_identity();    //ctm = fz_concat(ctm, fz_translate(0, -page->mediabox.y1));    ctm = fz_concat(ctm, fz_translate(-page->mediabox.x0, -page->mediabox.y1));    ctm = fz_concat(ctm, fz_scale(zoom, -zoom));    rotate += page->rotate;    if (rotate != 0)        ctm = fz_concat(ctm, fz_rotate(rotate));    return ctm;}PdfEngine::PdfEngine() :         _fileName(0)        , _pageCount(INVALID_PAGE_NO)         , _xref(NULL)        , _outline(NULL)        , _pageTree(NULL)        , _pages(NULL)        , _rast(NULL){    _getPageSem = CreateSemaphore(NULL, 1, 1, NULL);}PdfEngine::~PdfEngine(){    if (_pages) {        for (int i=0; i < _pageCount; i++) {            if (_pages[i])                pdf_droppage(_pages[i]);        }        free(_pages);    }    if (_pageTree)        pdf_droppagetree(_pageTree);    if (_outline)        pdf_dropoutline(_outline);    if (_xref) {        if (_xref->store)            pdf_dropstore(_xref->store);        _xref->store = 0;        pdf_closexref(_xref);    }    if (_rast)        fz_droprenderer(_rast);    CloseHandle(_getPageSem);    free((void*)_fileName);}bool PdfEngine::load(const WCHAR *fileName, WindowInfo *win, bool tryrepair){    _windowInfo = win;    setFileName(fileName);    fz_error *error = pdf_newxref(&_xref);    if (error)        goto Error;    error = pdf_loadxrefw(_xref, fileName);    if (error) {        if (!strncmp(error->msg, "ioerror", 7))            goto Error;        if (tryrepair) {            error = pdf_repairxref(_xref, (char*)fileName);            if (error)                goto Error;        }    }    error = pdf_decryptxref(_xref);    if (error)        goto Error;    if (_xref->crypt) {        int okay = pdf_setpassword(_xref->crypt, "");        if (okay)            goto DecryptedOk;        if (!win) {            // win might not be given if called from pdfbench.cc            goto Error;        }        for (int i=0; i<3; i++) {            char *pwd = GetPasswordForFile(win, fileName);            if (!pwd) {                // password not given                goto Error;            }            okay = pdf_setpassword(_xref->crypt, pwd);            free(pwd);            if (okay)                goto DecryptedOk;        }        goto Error;    }DecryptedOk:    error = pdf_loadpagetree(&_pageTree, _xref);    if (error)        goto Error;    /*     * Load meta information     * TODO: move this into mupdf library     * TODO: more descriptive errors?     */    fz_obj *obj;    obj = fz_dictgets(_xref->trailer, "Root");    if (!obj)        goto Error;    error = pdf_loadindirect(&_xref->root, _xref, obj);    if (error)        goto Error;    obj = fz_dictgets(_xref->trailer, "Info");    if (obj)    {        error = pdf_loadindirect(&_xref->info, _xref, obj);        if (error)            goto Error;    }    error = pdf_loadnametrees(_xref);    if (error)        goto Error;    error = pdf_loadoutline(&_outline, _xref);    if (error)        goto Error;    _pageCount = _pageTree->count;    _pages = (pdf_page**)malloc(sizeof(pdf_page*) * _pageCount);    for (int i = 0; i < _pageCount; i++)        _pages[i] = NULL;    return true;Error:    return false;}PdfTocItem *PdfEngine::buildTocTree(pdf_outline *entry){    wchar_t *name = utf8_to_utf16(entry->title);    PdfTocItem *node = new PdfTocItem(name, entry->link);    if (entry->child)        node->AddChild(buildTocTree(entry->child));        if (entry->next)        node->AddSibling(buildTocTree(entry->next));    return node;}PdfTocItem *PdfEngine::getTocTree(){    if (!hasTocTree())        return NULL;    return buildTocTree(_outline);}int PdfEngine::findPageNo(fz_obj *dest){    int p = 0;    if (fz_isint(dest)) {        p = fz_toint(dest);        return p+1;    }    int n = fz_tonum(dest);    int g = fz_togen(dest);    for (p = 0; p < _pageCount; p++)    {        fz_obj *page = _pageTree->pref[p];        int np = fz_tonum(page);        int gp = fz_togen(page);        if (n == np && g == gp)            return p + 1;    }    return 0;}fz_obj *PdfEngine::getNamedDest(const char *name){    fz_obj *obj = fz_dictgets(_xref->dests, (char*)name);    if (obj)        pdf_resolve(&obj, _xref);    return resolvedest(_xref, obj);}pdf_page *PdfEngine::getPdfPage(int pageNo){    if (!_pages)        return NULL;    WaitForSingleObject(_getPageSem, INFINITE);    pdf_page* page = _pages[pageNo-1];    if (page) {        if (!ReleaseSemaphore(_getPageSem, 1, NULL))            DBG_OUT("Fitz: ReleaseSemaphore error!\n");        return page;    }    // TODO: should check for error from pdf_getpageobject?    fz_obj * obj = pdf_getpageobject(_pageTree, pageNo - 1);    fz_error * error = pdf_loadpage(&page, _xref, obj);    if (error) {        if (!ReleaseSemaphore(_getPageSem, 1, NULL))            DBG_OUT("Fitz: ReleaseSemaphore error!\n");        fz_droperror(error);        return NULL;    }    _pages[pageNo-1] = page;    if (!ReleaseSemaphore(_getPageSem, 1, NULL))        DBG_OUT("Fitz: ReleaseSemaphore error!\n");    return page;}void PdfEngine::dropPdfPage(int pageNo){    assert(_pages);    if (!_pages) return;    pdf_page* page = _pages[pageNo-1];    assert(page);    if (!page) return;    pdf_droppage(page);    _pages[pageNo-1] = NULL;}int PdfEngine::pageRotation(int pageNo){    assert(validPageNo(pageNo));    fz_obj *dict = pdf_getpageobject(pages(), pageNo - 1);    int rotation;    fz_error *error = pdf_getpageinfo(_xref, dict, NULL, &rotation);    if (error)        return INVALID_ROTATION;    return rotation;}SizeD PdfEngine::pageSize(int pageNo){    assert(validPageNo(pageNo));    fz_obj *dict = pdf_getpageobject(pages(), pageNo - 1);    fz_rect bbox;    fz_error *error = pdf_getpageinfo(_xref, dict, &bbox, NULL);    if (error)        return SizeD(0,0);    return SizeD(fabs(bbox.x1 - bbox.x0), fabs(bbox.y1 - bbox.y0));}bool PdfEngine::printingAllowed(){    assert(_xref);    int permissionFlags = PDF_DEFAULT_PERM_FLAGS;    if (_xref && _xref->crypt)        permissionFlags = _xref->crypt->p;    if (permissionFlags & PDF_PERM_PRINT)        return true;    return false;}static void ConvertPixmapForWindows(fz_pixmap *image){   int bmpstride = ((image->w * 3 + 3) / 4) * 4;   int imageh = image->h;   int imagew = image->w;   unsigned char *bmpdata = (unsigned char*)fz_malloc(image->h * bmpstride);   if (!bmpdata)       return;   unsigned char *p = bmpdata;   unsigned char *s = image->samples;   for (int y = 0; y < imageh; y++)   {       unsigned char *pl = p;       unsigned char *sl = s;       for (int x = 0; x < imagew; x++)       {           pl[0] = sl[3];           pl[1] = sl[2];           pl[2] = sl[1];           pl += 3;           sl += 4;       }       p += bmpstride;       s += imagew * 4;   }   fz_free(image->samples);   image->samples = bmpdata;}RenderedBitmap *PdfEngine::renderBitmap(                           int pageNo, double zoomReal, int rotation,                           BOOL (*abortCheckCbkA)(void *data),                           void *abortCheckCbkDataA){    fz_error* error;    fz_matrix ctm;    fz_rect bbox;    if (!_rast) {        error = fz_newrenderer(&_rast, pdf_devicergb, 0, 1024 * 512);    }    fz_pixmap* image = NULL;    pdf_page* page = getPdfPage(pageNo);    if (!page)        goto Error;    zoomReal = zoomReal / 100.0;    ctm = viewctm(page, zoomReal, rotation);    bbox = fz_transformaabb(ctm, page->mediabox);    error = fz_rendertree(&image, _rast, page->tree, ctm, fz_roundrect(bbox), 1);#if CONSERVE_MEMORY    dropPdfPage(pageNo);#endif    if (error)        goto Error;    ConvertPixmapForWindows(image);    return new RenderedBitmap(image);Error:    return NULL;}static int linksLinkCount(pdf_link *currLink) {    if (!currLink)        return 0;    int count = 1;    while (currLink->next) {        ++count;        currLink = currLink->next;    }    return count;}/* Return number of all links in all loaded pages */int PdfEngine::linkCount() {    if (!_pages)        return 0;    int count = 0;    for (int i=0; i < _pageCount; i++)    {        pdf_page* page = _pages[i];        if (page)            count += linksLinkCount(page->links);    }    return count;}void PdfEngine::fillPdfLinks(PdfLink *pdfLinks, int linkCount){    pdf_link *link = 0;    int pageNo;    PdfLink *pdfLink = pdfLinks;    int linkNo = 0;    for (pageNo=0; pageNo < _pageCount; pageNo++)    {        pdf_page* page = _pages[pageNo];        if (!page)            continue;        link = page->links;        while (link)        {            assert(link);            pdfLink->pageNo = pageNo + 1;            pdfLink->link = link;            pdfLink->rectPage.x = link->rect.x0;            pdfLink->rectPage.y = link->rect.y0;            assert(link->rect.x1 >= link->rect.x0);            pdfLink->rectPage.dx = link->rect.x1 - link->rect.x0;            assert(pdfLink->rectPage.dx >= 0);            assert(link->rect.y1 >= link->rect.y0);            pdfLink->rectPage.dy = link->rect.y1 - link->rect.y0;            assert(pdfLink->rectPage.dy >= 0);            link = link->next;            linkNo++;            pdfLink++;        }    }    assert(linkCount == linkNo);}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?