/* Copyright Krzysztof Kowalczyk 2006-2007 License: GPLv2 */ #ifndef _DISPLAY_MODEL_H_ #define _DISPLAY_MODEL_H_ #include "base_util.h" #include "geom_util.h" #include "DisplayState.h" #include "PdfEngine.h" class GooString; class Link; class Links; class TextPage; class UGooString; class SplashBitmap; #define INVALID_ZOOM -99 #define INVALID_BIG_ZOOM 999999.0 /* arbitrary but big */ typedef struct DisplaySettings { int paddingPageBorderTop; int paddingPageBorderBottom; int paddingPageBorderLeft; int paddingPageBorderRight; int paddingBetweenPagesX; int paddingBetweenPagesY; } DisplaySettings; /* the default distance between a page and window border edges, in pixels */ #ifdef _WIN32 #define PADDING_PAGE_BORDER_TOP_DEF 6 #else #define PADDING_PAGE_BORDER_TOP_DEF 4 #endif #define PADDING_PAGE_BORDER_BOTTOM_DEF PADDING_PAGE_BORDER_TOP_DEF #define PADDING_PAGE_BORDER_LEFT_DEF 2 #define PADDING_PAGE_BORDER_RIGHT_DEF 2 /* the distance between pages in x axis, in pixels. Only applicable if columns > 1 */ #define PADDING_BETWEEN_PAGES_X_DEF 4 /* the distance between pages in y axis, in pixels. Only applicable if more than one page in y axis (continuous mode) */ #define PADDING_BETWEEN_PAGES_Y_DEF PADDING_PAGE_BORDER_TOP_DEF * 2 #define PADDING_PAGE_BORDER_TOP gDisplaySettings.paddingPageBorderTop #define PADDING_PAGE_BORDER_BOTTOM gDisplaySettings.paddingPageBorderBottom #define PADDING_PAGE_BORDER_LEFT gDisplaySettings.paddingPageBorderLeft #define PADDING_PAGE_BORDER_RIGHT gDisplaySettings.paddingPageBorderRight #define PADDING_BETWEEN_PAGES_X gDisplaySettings.paddingBetweenPagesX #define PADDING_BETWEEN_PAGES_Y gDisplaySettings.paddingBetweenPagesY #define POINT_OUT_OF_PAGE 0 /* Describes a link on PDF page. */ typedef struct PdfLink { int pageNo; /* on which Pdf page the link exists. 1..pageCount */ // TODO: remove it from common code, only splash needs it Link * link; /* a reference to a link; we don't own it */ RectD rectPage; /* position of the link on the page */ RectI rectCanvas; /* position of the link on canvas */ } PdfLink; /* Describes many attributes of one page in one, convenient place */ typedef struct PdfPageInfo { /* data that is constant for a given page. page size and rotation recorded in PDF file */ double pageDx; // TODO: consider SizeD instead of pageDx/pageDy double pageDy; int rotation; /* data that needs to be set before DisplayModel_relayout(). Determines whether a given page should be shown on the screen. */ bool shown; /* data that changes when zoom and rotation changes */ /* position and size within total area after applying zoom and rotation. Represents display rectangle for a given page. Calculated in DisplayModel_relayout() */ /* TODO: change it to RectD ?*/ double currDx; double currDy; double currPosX; double currPosY; /* data that changes due to scrolling. Calculated in DisplayModel_RecalcVisibleParts() */ bool visible; /* is currently visible on the page ? */ /* part of the image that should be shown */ int bitmapX, bitmapY, bitmapDx, bitmapDy; /* where it should be blitted on the screen */ int screenX, screenY; // TODO: remove it from common code, only splash needs it Links * links; TextPage * textPage; } PdfPageInfo; /* When searching, we can be in one of those states. The state determines what will happen after searching for next or previous term. */ enum SearchState { /* Search hasn't started yet. 'Next' will start searching from the top of current page, searching forward. 'Previous' will start searching from the top of current page, searching backward. */ eSsNone, /* We searched the whole document and didn't find the search term at all. 'Next' and 'prev' do nothing. */ eSsNotFound, /* Previous 'next' search found the term, without wrapping. 'Next' will continue searching forward from the current position. 'Previous' will search backward from the current position.*/ eSsFoundNext, /* Like eSsFoundNext but we wrapped past last page. In that case we show a message about being wrapped and continuing from top. */ eSsFoundNextWrapped, /* Previous 'prev' search found the term, without wrapping. 'Next' will search forward from the current position. 'Prev' will continue searching backward */ eSsFoundPrev, /* Like eSsFoundPrev, but wrapped around first page. */ eSsFoundPrevWrapped, /* TODO: add eSsFoundOnlyOne as optimization i.e. if the hit is the same as previous hit, 'next' and 'previous' will do nothing, to avoid (possibly long) no-op search. eSsFoundOnlyOne */ }; /* The current state of searching */ typedef struct SearchStateData { /* String we search for, both regular and unicode versions */ GooString * str; UGooString * strU; /* The page on which we started the search */ int startPage; /* did we wrap (crossed last page when searching forward or first page when searching backwards) */ BOOL wrapped; SearchState searchState; BOOL caseSensitive; int currPage; /* page for the last hit */ } SearchStateData; class DisplayModel { public: DisplayModel(DisplayMode displayMode); virtual ~DisplayModel(); RenderedBitmap *renderBitmap(int pageNo, double zoomReal, int rotation, BOOL (*abortCheckCbkA)(void *data), void *abortCheckCbkDataA) { if (!_pdfEngine) return NULL; return _pdfEngine->renderBitmap(pageNo, zoomReal, rotation, abortCheckCbkA, abortCheckCbkDataA); } PdfEngine *pdfEngine() { return _pdfEngine; } /* number of pages in PDF document */ int pageCount() const { return _pdfEngine->pageCount(); } bool load(const char *fileName, int startPage, WindowInfo *win); bool validPageNo(int pageNo) const { return _pdfEngine->validPageNo(pageNo); } /* current rotation selected by user */ int rotation(void) const { return _rotation; } void setRotation(int rotation) { _rotation = rotation; } DisplayMode displayMode() const { return _displayMode; } void changeDisplayMode(DisplayMode displayMode); const char *fileName(void) const { return _pdfEngine->fileName(); } bool fullScreen(void) const { return _fullScreen; } void setFullScreen(bool fullScreen) { _fullScreen = fullScreen; } /* a "virtual" zoom level. Can be either a real zoom level in percent (i.e. 100.0 is original size) or one of virtual values ZOOM_FIT_PAGE or ZOOM_FIT_WIDTH, whose real value depends on draw area size */ double zoomVirtual(void) const { return _zoomVirtual; } void setZoomVirtual(double zoomVirtual); double zoomReal(void) const { return _zoomReal; } int startPage(void) const { return _startPage; } /* TODO: should become non-virtual */ int currentPageNo(void) const; /* an arbitrary pointer that can be used by an app e.g. a multi-window GUI could link this to a data describing window displaying this document */ void * appData() const { return _appData; } void setAppData(void *appData) { _appData = appData; } /* TODO: rename to pageInfo() */ PdfPageInfo * getPageInfo(int pageNo) const; /* an array of PdfPageInfo, len of array is pageCount */ PdfPageInfo * pagesInfo; /* areaOffset is "polymorphic". If drawAreaSize.dx > totalAreSize.dx then areaOffset.x is offset of total area rect inside draw area, otherwise an offset of draw area inside total area. The same for areaOff.y, except it's for dy */ PointD areaOffset; /* size of draw area i.e. _totalDrawAreaSize minus scrollbarsSize (if they're shown) */ SizeD drawAreaSize; SearchStateData searchState; int searchHitPageNo; RectD searchHitRectPage; RectI searchHitRectCanvas; void setScrollbarsSize(int scrollbarXDy, int scrollbarYDx) { _scrollbarXDy = scrollbarXDy; _scrollbarYDx = scrollbarYDx; } void setTotalDrawAreaSize(SizeD size) { _totalDrawAreaSize = size; drawAreaSize = SizeD(size.dx() - _scrollbarYDx, size.dy() - _scrollbarXDy); } void changeTotalDrawAreaSize(SizeD totalDrawAreaSize); bool pageShown(int pageNo); bool pageVisible(int pageNo); bool pageVisibleNearby(int pageNo); void relayout(double zoomVirtual, int rotation); void goToPage(int pageNo, int scrollY, int scrollX=-1); bool goToPrevPage(int scrollY); bool goToNextPage(int scrollY); bool goToFirstPage(void); bool goToLastPage(void); void scrollXTo(int xOff); void scrollXBy(int dx); void scrollYTo(int yOff); void scrollYBy(int dy, bool changePage); void scrollYByAreaDy(bool forward, bool changePage); void zoomTo(double zoomVirtual); void zoomBy(double zoomFactor); void rotateBy(int rotation); virtual int getTextInRegion(int pageNo, RectD *region, unsigned short *buf, int buflen) = 0; void clearSearchHit(void); void setSearchHit(int pageNo, RectD *hitRect); void recalcLinksCanvasPos(void); int linkCount(void) const { return _linkCount; } PdfLink * link(int linkNo) const { return &(_links[linkNo]); } PdfLink * linkAtPosition(int x, int y); virtual void handleLink(PdfLink *pdfLink) = 0; virtual void cvtUserToScreen(int pageNo, double *x, double *y) = 0; virtual void cvtScreenToUser(int *pageNo, double *x, double *y) = 0; void rectCvtUserToScreen(int pageNo, RectD *r); void rectCvtScreenToUser(int *pageNo, RectD *r); protected: int getPageNoByPoint (double x, double y); void startRenderingPage(int pageNo); bool buildPagesInfo(void); double zoomRealFromFirtualForPage(double zoomVirtual, int pageNo); int firstVisiblePageNo(void) const; void changeStartPage(int startPage); void recalcVisibleParts(void); void recalcSearchHitCanvasPos(void); void renderVisibleParts(void); /* Those need to be implemented somewhere else by the GUI */ void setScrollbarsState(void); /* called when a page number changes */ void pageChanged(void); /* called when we decide that the display needs to be redrawn */ void repaintDisplay(bool delayed); void showBusyCursor(); void showNormalCursor(); PdfEngine * _pdfEngine; DisplayMode _displayMode; /* TODO: not used yet */ bool _fullScreen; /* In non-continuous mode is the first page from a PDF file that we're displaying. No meaning in continous mode. */ int _startPage; void * _appData; /* size of scrollbars */ int _scrollbarXDy; int _scrollbarYDx; /* size of total draw area (i.e. window size) */ SizeD _totalDrawAreaSize; /* size of virtual canvas containing all rendered pages. TODO: re-consider, 32 signed number should be large enough for everything. */ SizeD _canvasSize; /* real zoom value calculated from zoomVirtual. Same as zoomVirtual except for ZOOM_FIT_PAGE and ZOOM_FIT_WIDTH */ double _zoomReal; double _zoomVirtual; int _rotation; /* total number of links */ int _linkCount; /* an array of 'totalLinksCount' size, each entry describing a link */ PdfLink * _links; }; bool validZoomReal(double zoomReal); bool displayModeContinuous(DisplayMode displayMode); bool displayModeFacing(DisplayMode displayMode); DisplaySettings * globalDisplaySettings(void); int columnsFromDisplayMode(DisplayMode displayMode); void pageSizeAfterRotation(PdfPageInfo *pageInfo, int rotation, double *pageDxOut, double *pageDyOut); bool displayStateFromDisplayModel(DisplayState *ds, DisplayModel *dm); extern DisplaySettings gDisplaySettings; /* must be implemented somewhere else */ //extern void LaunchBrowser(const char *uri); /* We keep a cache of rendered bitmaps. BitmapCacheEntry keeps data that uniquely identifies rendered page (dm, pageNo, rotation, zoomReal) and corresponding rendered bitmap. */ typedef struct { DisplayModel * dm; int pageNo; int rotation; double zoomLevel; RenderedBitmap *bitmap; double renderTime; } BitmapCacheEntry; typedef struct { DisplayModel * dm; int pageNo; double zoomLevel; int rotation; int abort; } PageRenderRequest; /* Lock protecting both bitmap cache and page render queue */ void LockCache(); void UnlockCache(); void RenderQueue_Add(DisplayModel *dm, int pageNo); extern void RenderQueue_RemoveForDisplayModel(DisplayModel *dm); extern void cancelRenderingForDisplayModel(DisplayModel *dm); BitmapCacheEntry *BitmapCache_Find(DisplayModel *dm, int pageNo, double zoomLevel, int rotation); BitmapCacheEntry *BitmapCache_Find(DisplayModel *dm, int pageNo); bool BitmapCache_Exists(DisplayModel *dm, int pageNo, double zoomLevel, int rotation); void BitmapCache_Add(DisplayModel *dm, int pageNo, double zoomLevel, int rotation, RenderedBitmap *bitmap, double renderTime); void BitmapCache_FreeAll(void); bool BitmapCache_FreeForDisplayModel(DisplayModel *dm); bool BitmapCache_FreeNotVisible(void); #endif