📄 displaymodel.cc.svn-base
字号:
setZoomVirtual(zoomVirtual);
// DBG_OUT("DisplayModel::relayout(), pageCount=%d, zoomReal=%.6f, zoomVirtual=%.2f\n", pageCount, dm->zoomReal, dm->zoomVirtual);
totalAreaDx = 0;
if (0 == currZoomReal)
newAreaOffsetX = 0.0;
else
newAreaOffsetX = areaOffset.x * _zoomReal / currZoomReal;
areaOffset.x = newAreaOffsetX;
/* calculate the position of each page on the canvas, given current zoom,
rotation, columns parameters. You can think of it as a simple
table layout i.e. rows with a fixed number of columns. */
columns = columnsFromDisplayMode(displayMode());
columnsLeft = columns;
currPosX = PADDING_PAGE_BORDER_LEFT;
rowMaxPageDy = 0;
for (pageNo = 1; pageNo <= pageCount(); ++pageNo) {
pageInfo = getPageInfo(pageNo);
if (!pageInfo->shown) {
assert(!pageInfo->visible);
continue;
}
pageSizeAfterRotation(pageInfo, rotation, &pageDx, &pageDy);
currDxInt = (int)(pageDx * _zoomReal * 0.01 + 0.5);
currDyInt = (int)(pageDy * _zoomReal * 0.01 + 0.5);
pageInfo->currDx = (double)currDxInt;
pageInfo->currDy = (double)currDyInt;
if (rowMaxPageDy < pageInfo->currDy)
rowMaxPageDy = pageInfo->currDy;
pageInfo->currPosX = currPosX;
pageInfo->currPosY = currPosY;
/* set position of the next page to be after this page with padding.
Note: for the last page we don't want padding so we'll have to
substract it when we create new page */
currPosX += (pageInfo->currDx + PADDING_BETWEEN_PAGES_X);
--columnsLeft;
assert(columnsLeft >= 0);
if (0 == columnsLeft) {
/* starting next row */
currPosY += rowMaxPageDy + PADDING_BETWEEN_PAGES_Y;
rowMaxPageDy = 0;
thisRowDx = currPosX - PADDING_BETWEEN_PAGES_X + PADDING_PAGE_BORDER_RIGHT;
if (totalAreaDx < thisRowDx)
totalAreaDx = thisRowDx;
columnsLeft = columns;
currPosX = PADDING_PAGE_BORDER_LEFT;
}
/* DBG_OUT(" page = %3d, (x=%3d, y=%5d, dx=%4d, dy=%4d) orig=(dx=%d,dy=%d)\n",
pageNo, (int)pageInfo->currPosX, (int)pageInfo->currPosY,
(int)pageInfo->currDx, (int)pageInfo->currDy,
(int)pageDx, (int)pageDy); */
}
if (columnsLeft < columns) {
/* this is a partial row */
currPosY += rowMaxPageDy + PADDING_BETWEEN_PAGES_Y;
thisRowDx = currPosX + (pageInfo->currDx + PADDING_BETWEEN_PAGES_X) - PADDING_BETWEEN_PAGES_X + PADDING_PAGE_BORDER_RIGHT;
if (totalAreaDx < thisRowDx)
totalAreaDx = thisRowDx;
}
/* since pages can be smaller than the drawing area, center them in x axis */
if (totalAreaDx < drawAreaSize.dx()) {
areaOffset.x = 0.0;
offX = (drawAreaSize.dx() - totalAreaDx) / 2.0 + PADDING_PAGE_BORDER_LEFT;
assert(offX >= 0.0);
areaPerPageDx = totalAreaDx - PADDING_PAGE_BORDER_LEFT - PADDING_PAGE_BORDER_RIGHT;
areaPerPageDx = areaPerPageDx - (PADDING_BETWEEN_PAGES_X * (columns - 1));
areaPerPageDxInt = (int)(areaPerPageDx / (double)columns);
areaPerPageDx = (double)areaPerPageDxInt;
totalAreaDx = drawAreaSize.dx();
pageInARow = 0;
for (pageNo = 1; pageNo <= pageCount(); ++pageNo) {
pageInfo = getPageInfo(pageNo);
if (!pageInfo->shown) {
assert(!pageInfo->visible);
continue;
}
pageOffX = (pageInARow * (PADDING_BETWEEN_PAGES_X + areaPerPageDx));
pageOffX += (areaPerPageDx - pageInfo->currDx) / 2;
assert(pageOffX >= 0.0);
pageInfo->currPosX = pageOffX + offX;
++pageInARow;
if (pageInARow == columns)
pageInARow = 0;
}
}
/* if after resizing we would have blank space on the right due to x offset
being too much, make x offset smaller so that there's no blank space */
if (drawAreaSize.dx() - (totalAreaDx - newAreaOffsetX) > 0) {
newAreaOffsetX = totalAreaDx - drawAreaSize.dx();
areaOffset.x = newAreaOffsetX;
}
/* if a page is smaller than drawing area in y axis, y-center the page */
totalAreaDy = currPosY + PADDING_PAGE_BORDER_BOTTOM - PADDING_BETWEEN_PAGES_Y;
if (totalAreaDy < drawAreaSize.dy()) {
offY = PADDING_PAGE_BORDER_TOP + (drawAreaSize.dy() - totalAreaDy) / 2;
DBG_OUT(" offY = %.2f\n", offY);
assert(offY >= 0.0);
totalAreaDy = drawAreaSize.dy();
for (pageNo = 1; pageNo <= pageCount(); ++pageNo) {
pageInfo = getPageInfo(pageNo);
if (!pageInfo->shown) {
assert(!pageInfo->visible);
continue;
}
pageInfo->currPosY += offY;
DBG_OUT(" page = %3d, (x=%3d, y=%5d, dx=%4d, dy=%4d) orig=(dx=%d,dy=%d)\n",
pageNo, (int)pageInfo->currPosX, (int)pageInfo->currPosY,
(int)pageInfo->currDx, (int)pageInfo->currDy,
(int)pageDx, (int)pageDy);
}
}
_canvasSize = SizeD(totalAreaDx, totalAreaDy);
}
void DisplayModel::changeStartPage(int startPage)
{
assert(validPageNo(startPage));
assert(!displayModeContinuous(displayMode()));
int columns = columnsFromDisplayMode(displayMode());
_startPage = startPage;
for (int pageNo = 1; pageNo <= pageCount(); pageNo++) {
PdfPageInfo *pageInfo = getPageInfo(pageNo);
if (displayModeContinuous(displayMode()))
pageInfo->shown = true;
else
pageInfo->shown = false;
if ((pageNo >= startPage) && (pageNo < startPage + columns)) {
//DBG_OUT("DisplayModel::changeStartPage() set page %d as shown\n", pageNo);
pageInfo->shown = true;
}
pageInfo->visible = false;
}
relayout(zoomVirtual(), rotation());
}
/* Given positions of each page in a large sheet that is continous view and
coordinates of a current view into that large sheet, calculate which
parts of each page is visible on the screen.
Needs to be recalucated after scrolling the view. */
void DisplayModel::recalcVisibleParts(void)
{
int pageNo;
RectI drawAreaRect;
RectI pageRect;
RectI intersect;
PdfPageInfo* pageInfo;
assert(_pagesInfo);
if (!_pagesInfo)
return;
drawAreaRect.x = (int)areaOffset.x;
drawAreaRect.y = (int)areaOffset.y;
drawAreaRect.dx = drawAreaSize.dxI();
drawAreaRect.dy = drawAreaSize.dyI();
// DBG_OUT("DisplayModel::recalcVisibleParts() draw area (x=%3d,y=%3d,dx=%4d,dy=%4d)\n",
// drawAreaRect.x, drawAreaRect.y, drawAreaRect.dx, drawAreaRect.dy);
for (pageNo = 1; pageNo <= pageCount(); ++pageNo) {
pageInfo = getPageInfo(pageNo);
if (!pageInfo->shown) {
assert(!pageInfo->visible);
continue;
}
pageRect.x = (int)pageInfo->currPosX;
pageRect.y = (int)pageInfo->currPosY;
pageRect.dx = (int)pageInfo->currDx;
pageRect.dy = (int)pageInfo->currDy;
pageInfo->visible = false;
if (RectI_Intersect(&pageRect, &drawAreaRect, &intersect)) {
pageInfo->visible = true;
pageInfo->bitmapX = (int) ((double)intersect.x - pageInfo->currPosX);
assert(pageInfo->bitmapX >= 0);
pageInfo->bitmapY = (int) ((double)intersect.y - pageInfo->currPosY);
assert(pageInfo->bitmapY >= 0);
pageInfo->bitmapDx = intersect.dx;
pageInfo->bitmapDy = intersect.dy;
pageInfo->screenX = (int) ((double)intersect.x - areaOffset.x);
assert(pageInfo->screenX >= 0);
assert(pageInfo->screenX <= drawAreaSize.dx());
pageInfo->screenY = (int) ((double)intersect.y - areaOffset.y);
assert(pageInfo->screenX >= 0);
assert(pageInfo->screenY <= drawAreaSize.dy());
/* DBG_OUT(" visible page = %d, (x=%3d,y=%3d,dx=%4d,dy=%4d) at (x=%d,y=%d)\n",
pageNo, pageInfo->bitmapX, pageInfo->bitmapY,
pageInfo->bitmapDx, pageInfo->bitmapDy,
pageInfo->screenX, pageInfo->screenY); */
}
}
}
/* Map rectangle <r> on the page <pageNo> to point on the screen. */
bool DisplayModel::rectCvtUserToScreen(int pageNo, RectD *r)
{
double sx, sy, ex, ey;
sx = r->x;
sy = r->y;
ex = r->x + r->dx;
ey = r->y + r->dy;
bool ok = cvtUserToScreen(pageNo, &sx, &sy);
ok = ok && cvtUserToScreen(pageNo, &ex, &ey);
ok = ok && RectD_FromXY(r, sx, ex, sy, ey);
return ok;
}
/* Map rectangle <r> on the page <pageNo> to point on the screen. */
bool DisplayModel::rectCvtScreenToUser(int *pageNo, RectD *r)
{
double sx, sy, ex, ey;
sx = r->x;
sy = r->y;
ex = r->x + r->dx;
ey = r->y + r->dy;
return cvtScreenToUser(pageNo, &sx, &sy)
&& cvtScreenToUser(pageNo, &ex, &ey)
&& RectD_FromXY(r, sx, ex, sy, ey);
}
int DisplayModel::getPageNoByPoint (double x, double y)
{
for (int pageNo = 1; pageNo <= pageCount(); ++pageNo) {
PdfPageInfo *pageInfo = getPageInfo(pageNo);
if (!pageInfo->visible)
continue;
assert(pageInfo->shown);
if (!pageInfo->shown)
continue;
RectI pageOnScreen;
pageOnScreen.x = pageInfo->screenX;
pageOnScreen.y = pageInfo->screenY;
pageOnScreen.dx = pageInfo->bitmapDx;
pageOnScreen.dy = pageInfo->bitmapDy;
if (RectI_Inside (&pageOnScreen, (int)x, (int)y))
return pageNo;
}
return POINT_OUT_OF_PAGE;
}
void DisplayModel::recalcSearchHitCanvasPos(void)
{
int pageNo;
RectD rect;
pageNo = searchHitPageNo;
if (INVALID_PAGE_NO == pageNo) return;
rect = searchHitRectPage;
rectCvtUserToScreen(pageNo, &rect);
searchHitRectCanvas.x = (int)rect.x;
searchHitRectCanvas.y = (int)rect.y;
searchHitRectCanvas.dx = (int)rect.dx;
searchHitRectCanvas.dy = (int)rect.dy;
}
/* Recalculates the position of each link on the canvas i.e. applies current
rotation and zoom level and offsets it by the offset of each page in
the canvas.
TODO: applying rotation and zoom level could be split into a separate
function for speedup, since it only has to change after rotation/zoomLevel
changes while this function has to be called after each scrolling.
But I'm not sure if this would be a significant speedup */
void DisplayModel::recalcLinksCanvasPos(void)
{
PdfLink * pdfLink;
PdfPageInfo * pageInfo;
int linkNo;
RectD rect;
int linkCount = getLinkCount();
// TODO: calling it here is a bit of a hack
recalcSearchHitCanvasPos();
DBG_OUT("DisplayModel::recalcLinksCanvasPos() linkCount=%d\n", linkCount);
if (0 == linkCount)
return;
for (linkNo = 0; linkNo < linkCount; linkNo++) {
pdfLink = &_links[linkNo];
pageInfo = getPageInfo(pdfLink->pageNo);
if (!pageInfo->visible) {
/* hack: make the links on pages that are not shown invisible by
moving it off canvas. A better solution would probably be
not adding those links in the first place */
pdfLink->rectCanvas.x = -100;
pdfLink->rectCanvas.y = -100;
pdfLink->rectCanvas.dx = 0;
pdfLink->rectCanvas.dy = 0;
continue;
}
rect = pdfLink->rectPage;
rectCvtUserToScreen(pdfLink->pageNo, &rect);
#if 0 // this version is correct but needs to be made generic, not specific to poppler
/* hack: in PDFs that have a crop-box (like treo700psprint_UG.pdf)
we need to shift links by the offset of crop-box. Since we do it
after conversion involving ctm, we need to apply current zoom and
rotation. This is probably not the best place to be doing this
but it's the only one we managed to make work */
double offX = dm->pdfDoc->getCatalog()->getPage(pdfLink->pageNo)->getCropBox()->x1;
double offY = dm->pdfDoc->getCatalog()->getPage(pdfLink->pageNo)->getCropBox()->y1;
if (flippedRotation(dm->rotation)) {
double tmp = offX;
offX = offY;
offY = tmp;
}
offX = offX * dm->zoomReal * 0.01;
offY = offY * dm->zoomReal * 0.01;
#else
pdfLink->rectCanvas.x = (int)rect.x;
pdfLink->rectCanvas.y = (int)rect.y;
pdfLink->rectCanvas.dx = (int)rect.dx;
assert(pdfLink->rectCanvas.dx >= 0);
pdfLink->rectCanvas.dy = (int)rect.dy;
assert(pdfLink->rectCanvas.dy >= 0);
#endif
DBG_OUT(" link on page (x=%d, y=%d, dx=%d, dy=%d),\n",
(int)pdfLink->rectPage.x, (int)pdfLink->rectPage.y,
(int)pdfLink->rectPage.dx, (int)pdfLink->rectPage.dy);
DBG_OUT(" screen (x=%d, y=%d, dx=%d, dy=%d)\n",
(int)rect.x, (int)rect.y,
(int)rect.dx, (int)rect.dy);
}
}
void DisplayModel::clearSearchHit(void)
{
DBG_OUT("DisplayModel::clearSearchHit()\n");
searchHitPageNo = INVALID_PAGE_NO;
}
void DisplayModel::setSearchHit(int pageNo, RectD *hitRect)
{
//DBG_OUT("DisplayModel::setSearchHit() page=%d at pos (%.2f, %.2f)-(%.2f,%.2f)\n", pageNo, xs, ys, xe, ye);
searchHitPageNo = pageNo;
searchHitRectPage = *hitRect;
recalcSearchHitCanvasPos();
}
/* Given position 'x'/'y' in the draw area, returns a structure describing
a link or NULL if there is no link at this position.
Note: DisplayModelSplash owns this memory so it should not be changed by the
caller and caller should not reference it after it has changed (i.e. process
it immediately since it will become invalid after each _relayout()).
TODO: this function is called frequently from UI code so make sure that
it's fast enough for a decent number of link.
Possible speed improvement: remember which links are visible after
scrolling and skip the _Inside test for those invisible.
Another way: build another list with only those visible, so we don't
even have to travers those that are invisible.
*/
PdfLink *DisplayModel::linkAtPosition(int x, int y)
{
int linkCount = getLinkCount();
if (0 == linkCount) return NULL;
int canvasPosX = x;
int canvasPosY = y;
for (int i = 0; i < linkCount; i++) {
PdfLink *currLink = &_links[i];
if (RectI_Inside(&(currLink->rectCanvas), canvasPosX, canvasPosY))
return currLink;
}
return NULL;
}
/* Send the request to render a given page to a rendering thread */
void DisplayModel::startRenderingPage(int pageNo)
{
RenderQueue_Add(this, pageNo);
}
void DisplayModel::renderVisibleParts(void)
{
int pageNo;
PdfPageInfo* pageInfo;
int lastVisible = 0;
// DBG_OUT("DisplayModel::renderVisibleParts()\n");
for (pageNo = 1; pageNo <= pageCount(); ++pageNo) {
pageInfo = getPageInfo(pageNo);
if (pageInfo->visible) {
assert(pageInfo->shown);
startRenderingPage(pageNo);
lastVisible = pageNo;
}
}
#ifdef PREDICTIVE_RENDER
if (0 != lastVisible && lastVisible != pageCount())
startRenderingPage(lastVisible+1);
#endif
}
void DisplayModel::changeTotalDrawAreaSize(SizeD totalDrawAreaSize)
{
int newPageNo;
int currPageNo;
currPageNo = currentPageNo();
setTotalDrawAreaSize(totalDrawAreaSize);
relayout(zoomVirtual(), rotation());
recalcVisibleParts();
recalcLinksCanvasPos();
renderVisibleParts();
setScrollbarsState();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -