⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 displaymodel.cc.svn-base

📁 SumatraPDF是一款小型开源的pdf阅读工具。虽然玲珑小巧(只有800多KB)
💻 SVN-BASE
📖 第 1 页 / 共 4 页
字号:
/* Copyright Krzysztof Kowalczyk 2006-2007
   License: GPLv2 */

/* How to think of display logic: physical screen of size
   drawAreaSize is a window into (possibly much larger)
   total area (canvas) of size canvasSize.

   In DM_SINGLE_PAGE mode total area is the size of currently displayed page
   given current zoomLovel and rotation.
   In DM_CONTINUOUS mode total area consist of all pages rendered sequentially
   with a given zoomLevel and rotation. totalAreaDy is the sum of heights
   of all pages plus spaces between them and totalAreaDx is the size of
   the widest page.

   A possible configuration could look like this:

 -----------------------------------
 |                                 |
 |          -------------          |
 |          | screen    |          |
 |          | i.e.      |          |
 |          | drawArea  |          |
 |          -------------          |
 |                                 |
 |                                 |
 |    canvas                       |
 |                                 |
 |                                 |
 |                                 |
 |                                 |
 -----------------------------------

  We calculate the total area size and position of each page we display on the
  canvas. Canvas has to be >= draw area.

  Changing zoomLevel or rotation requires recalculation of total area and
  position of pdf pages in it.

  We keep the offset of draw area relative to total area. The offset changes
  due to scrolling (with keys or using scrollbars).

  To draw we calculate which part of each page overlaps draw area, we render
  those pages to a bitmap and display those bitmaps.
*/

#include "DisplayModel.h"

#include "str_util.h"
#include "wstr_util.h"

#include <assert.h>
#include <stdlib.h>

#ifdef _WIN32
#define PREDICTIVE_RENDER 1
#endif

#define MAX_BITMAPS_CACHED 256

static CRITICAL_SECTION     cacheMutex;
static int cacheMutexInitialized = 0;
static BitmapCacheEntry *   gBitmapCache[MAX_BITMAPS_CACHED] = {0};
static int                  gBitmapCacheCount = 0;

DisplaySettings gDisplaySettings = {
  PADDING_PAGE_BORDER_TOP_DEF,
  PADDING_PAGE_BORDER_BOTTOM_DEF,
  PADDING_PAGE_BORDER_LEFT_DEF,
  PADDING_PAGE_BORDER_RIGHT_DEF,
  PADDING_BETWEEN_PAGES_X_DEF,
  PADDING_BETWEEN_PAGES_Y_DEF
};

bool validZoomReal(double zoomReal)
{
    if ((zoomReal < ZOOM_MIN) || (zoomReal > ZOOM_MAX)) {
        DBG_OUT("validZoomReal() invalid zoom: %.4f\n", zoomReal);
        return false;
    }
    return true;
}

bool displayModeFacing(DisplayMode displayMode)
{
    if ((DM_SINGLE_PAGE == displayMode) || (DM_CONTINUOUS == displayMode))
        return false;
    else if ((DM_FACING == displayMode) || (DM_CONTINUOUS_FACING == displayMode))
        return true;
    assert(0);
    return false;
}

bool displayModeContinuous(DisplayMode displayMode)
{
    if ((DM_SINGLE_PAGE == displayMode) || (DM_FACING == displayMode))
        return false;
    else if ((DM_CONTINUOUS == displayMode) || (DM_CONTINUOUS_FACING == displayMode))
        return true;
    assert(0);
    return false;
}

int columnsFromDisplayMode(DisplayMode displayMode)
{
    if (DM_SINGLE_PAGE == displayMode) {
        return 1;
    } else if (DM_FACING == displayMode) {
        return 2;
    } else if (DM_CONTINUOUS == displayMode) {
        return 1;
    } else if (DM_CONTINUOUS_FACING == displayMode) {
        return 2;
    } else
        assert(0);
    return 1;
}

DisplaySettings *globalDisplaySettings(void)
{
    return &gDisplaySettings;
}

bool rotationFlipped(int rotation)
{
    assert(validRotation(rotation));
    normalizeRotation(&rotation);
    if ((90 == rotation) || (270 == rotation))
        return true;
    return false;
}

bool displayStateFromDisplayModel(DisplayState *ds, DisplayModel *dm)
{
    const char *fileNameUtf8 = wstr_to_utf8(dm->fileName());
    ds->filePath = str_escape(fileNameUtf8);
    if (!ds->filePath)
        return FALSE;
    ds->displayMode = dm->displayMode();
    ds->pageNo = dm->currentPageNo();
    ds->rotation = dm->rotation();
    ds->zoomVirtual = dm->zoomVirtual();
    ds->scrollX = (int)dm->areaOffset.x;
    if (displayModeContinuous(dm->displayMode())) {
        /* TODO: should be offset of top page */
        PdfPageInfo *pageInfo = dm->getPageInfo(ds->pageNo);
        if (pageInfo)                
            ds->scrollY = (int)(dm->areaOffset.y + PADDING_PAGE_BORDER_TOP - pageInfo->currPosY);
        else
            ds->scrollY = 0;

    } else {
        ds->scrollY = (int)dm->areaOffset.y;
    }
    ds->showToc = dm->_showToc;
    return TRUE;
}

/* Given 'pageInfo', which should contain correct information about
   pageDx, pageDy and rotation, return a page size after applying a global
   rotation */
void pageSizeAfterRotation(PdfPageInfo *pageInfo, int rotation,
    double *pageDxOut, double *pageDyOut)
{
    assert(pageInfo && pageDxOut && pageDyOut);
    if (!pageInfo || !pageDxOut || !pageDyOut)
        return;

    *pageDxOut = pageInfo->pageDx;
    *pageDyOut = pageInfo->pageDy;

    rotation = rotation + pageInfo->rotation;
    normalizeRotation(&rotation);
    if (rotationFlipped(rotation))
        swap_double(pageDxOut, pageDyOut);
}

DisplayModel::DisplayModel(DisplayMode displayMode)
{
    _displayMode = displayMode;
    _rotation = INVALID_ROTATION;
    _zoomVirtual = INVALID_ZOOM;
    _fullScreen = FALSE;
    _showToc = TRUE;
    _startPage = INVALID_PAGE_NO;
    _appData = NULL;
    pdfEngine = NULL;
    _pdfSearch = NULL;
    _pagesInfo = NULL;

    _links = NULL;
    _linksCount = 0;

    searchHitPageNo = INVALID_PAGE_NO;
    searchState.searchState = eSsNone;

    pdfEngine = new PdfEngine();
    _pdfSearch = new PdfSearch(pdfEngine);

}

DisplayModel::~DisplayModel()
{
    free(_pagesInfo);    
    free(_links);
    delete _pdfSearch;
    delete pdfEngine;
    RenderQueue_RemoveForDisplayModel(this);
    BitmapCache_FreeForDisplayModel(this);
    cancelRenderingForDisplayModel(this);
}

PdfPageInfo *DisplayModel::getPageInfo(int pageNo) const
{
    assert(validPageNo(pageNo));
    assert(_pagesInfo);
    if (!_pagesInfo) return NULL;
    return &(_pagesInfo[pageNo-1]);
}

bool DisplayModel::load(const WCHAR *fileName, int startPage, WindowInfo *win, bool tryrepair)
{ 
    assert(fileName);
    if (!pdfEngine->load(fileName, win, tryrepair))
          return false;

    if (validPageNo(startPage))
        _startPage = startPage;
    else
        _startPage = 1;

    if (!buildPagesInfo())
        return false;

    _pdfSearch->tracker = (PdfSearchTracker *)win;
    return true;
}

bool DisplayModel::buildPagesInfo(void)
{
    assert(!_pagesInfo);
    int _pageCount = pageCount();

    _pagesInfo = (PdfPageInfo*)calloc(1, _pageCount * sizeof(PdfPageInfo));
    if (!_pagesInfo)
        return false;

    for (int pageNo = 1; pageNo <= _pageCount; pageNo++) {
        PdfPageInfo *pageInfo = getPageInfo(pageNo);
        SizeD pageSize = pdfEngine->pageSize(pageNo);
        pageInfo->pageDx = pageSize.dx();
        pageInfo->pageDy = pageSize.dy();
        pageInfo->rotation = pdfEngine->pageRotation(pageNo);

// TODO: poppler removal. Do I need this at all?
//        pageInfo->links = NULL;

        pageInfo->visible = false;
        pageInfo->shown = false;
        if (displayModeContinuous(_displayMode)) {
            pageInfo->shown = true;
        } else {
            if ((pageNo >= _startPage) && (pageNo < _startPage + columnsFromDisplayMode(_displayMode))) {
                DBG_OUT("DisplayModelSplash::CreateFromPdfDoc() set page %d as shown\n", pageNo);
                pageInfo->shown = true;
            }
        }
    }
    return true;
}

bool DisplayModel::pageShown(int pageNo)
{
    PdfPageInfo *pageInfo = getPageInfo(pageNo);
    if (!pageInfo)
        return false;
    return pageInfo->shown;
}

bool DisplayModel::pageVisible(int pageNo)
{
    PdfPageInfo *pageInfo = getPageInfo(pageNo);
    if (!pageInfo)
        return false;
    return pageInfo->visible;
}

/* Return true if a page is visible or a page below or above is visible */
bool DisplayModel::pageVisibleNearby(int pageNo)
{
    /* TODO: should it check 2 pages above and below in facing mode? */
    if (pageVisible(pageNo))
        return true;
    if (validPageNo(pageNo-1) && pageVisible(pageNo-1))
        return true;
    if (validPageNo(pageNo+1) && pageVisible(pageNo+1))
        return true;
    return false;
}

/* Given a zoom level that can include a "virtual" zoom levels like ZOOM_FIT_WIDTH
   and ZOOM_FIT_PAGE, calculate an absolute zoom level */
double DisplayModel::zoomRealFromFirtualForPage(double zoomVirtual, int pageNo)
{
    double          _zoomReal, zoomX, zoomY, pageDx, pageDy;
    double          areaForPageDx, areaForPageDy;
    int             areaForPageDxInt;
    int             columns;

    pageSizeAfterRotation(getPageInfo(pageNo), rotation(), &pageDx, &pageDy);

    assert(0 != (int)pageDx);
    assert(0 != (int)pageDy);

    columns = columnsFromDisplayMode(displayMode());
    areaForPageDx = (drawAreaSize.dx() - PADDING_PAGE_BORDER_LEFT - PADDING_PAGE_BORDER_RIGHT);
    areaForPageDx -= (PADDING_BETWEEN_PAGES_X * (columns - 1));
    areaForPageDxInt = (int)(areaForPageDx / columns);
    areaForPageDx = (double)areaForPageDxInt;
    areaForPageDy = drawAreaSize.dy() - PADDING_PAGE_BORDER_TOP - PADDING_PAGE_BORDER_BOTTOM;
    if (ZOOM_FIT_WIDTH == zoomVirtual) {
        /* TODO: should use gWinDx if we don't show scrollbarY */
        if (areaForPageDx <= 0) {
            return 0;
        }
        _zoomReal = (areaForPageDx * 100.0) / (double)pageDx;
    } else if (ZOOM_FIT_PAGE == zoomVirtual) {
        if (areaForPageDx <= 0 || areaForPageDy <= 0) {
            return 0;
        }
        zoomX = (areaForPageDx * 100.0) / (double)pageDx;
        zoomY = (areaForPageDy * 100.0) / (double)pageDy;
        if (zoomX < zoomY)
            _zoomReal = zoomX;
        else
            _zoomReal= zoomY;
    } else
        _zoomReal = zoomVirtual;

    return _zoomReal;
}

int DisplayModel::firstVisiblePageNo(void) const
{
    assert(_pagesInfo);
    if (!_pagesInfo) return INVALID_PAGE_NO;

    for (int pageNo = 1; pageNo <= pageCount(); ++pageNo) {
        PdfPageInfo *pageInfo = getPageInfo(pageNo);
        if (pageInfo->visible)
            return pageNo;
    }
    
    /* If no pages are visible */
    return INVALID_PAGE_NO;
}

int DisplayModel::currentPageNo(void) const
{
    if (displayModeContinuous(displayMode()))
        return firstVisiblePageNo();
    else
        return _startPage;
}

void DisplayModel::setZoomVirtual(double zoomVirtual)
{
    int     pageNo;
    double  minZoom = INVALID_BIG_ZOOM;
    double  thisPageZoom;

    assert(ValidZoomVirtual(zoomVirtual));
    _zoomVirtual = zoomVirtual;

    if ((ZOOM_FIT_WIDTH == zoomVirtual) || (ZOOM_FIT_PAGE == zoomVirtual)) {
        /* we want the same zoom for all pages, so use the smallest zoom
           across the pages so that the largest page fits. In most PDFs all
           pages are the same size anyway */
        for (pageNo = 1; pageNo <= pageCount(); pageNo++) {
            if (pageShown(pageNo)) {
                thisPageZoom = zoomRealFromFirtualForPage(this->zoomVirtual(), pageNo);
                if (minZoom > thisPageZoom)
                    minZoom = thisPageZoom;
            }
        }
        assert(minZoom != INVALID_BIG_ZOOM);
        this->_zoomReal = minZoom;
    } else
        this->_zoomReal = zoomVirtual;
}

/* Given pdf info and zoom/rotation, calculate the position of each page on a
   large sheet that is continous view. Needs to be recalculated when:
     * zoom changes
     * rotation changes
     * switching between display modes
     * navigating to another page in non-continuous mode */
void DisplayModel::relayout(double zoomVirtual, int rotation)
{
    int         pageNo;
    PdfPageInfo*pageInfo = NULL;
    double      currPosX;
    double      pageDx=0, pageDy=0;
    int         currDxInt, currDyInt;
    double      totalAreaDx, totalAreaDy;
    double      areaPerPageDx;
    int         areaPerPageDxInt;
    double      thisRowDx;
    double      rowMaxPageDy;
    double      offX, offY;
    double      pageOffX;
    int         columnsLeft;
    int         pageInARow;
    int         columns;
    double      newAreaOffsetX;

    assert(_pagesInfo);
    if (!_pagesInfo)
        return;

    normalizeRotation(&rotation);
    assert(validRotation(rotation));

    _rotation = rotation;

    double currPosY = PADDING_PAGE_BORDER_TOP;
    double currZoomReal = _zoomReal;

⌨️ 快捷键说明

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