From 01c1d8ae41ab7382470d545b06945c094c382662 Mon Sep 17 00:00:00 2001 From: Brian Ewins Date: Sun, 19 Apr 2009 11:07:53 +0100 Subject: [PATCH] Fix bug 3188, text selection across table cells Bug 3188. When selecting text, poppler goes across the whole page then down, rather than across each cell, down that cell, then across to the next cell. This leads to illegible paste results. Teach TextPage to visit the selection in reading order rather than block order. Start with the first block in reading order whose bottom right lies below and right of either the start or end point of the selection. Finish at the first block containing the other selection point, or if there is no such block, at a block from the nearest column whose top lies closest to the stop point. When selecting text in reading order, it does not make sense to preserve formatting (since this places text out of order). The code that reformatted the selection text into columns has been removed. --- poppler/TextOutputDev.cc | 198 +++++++++++++++++++++++----------------------- 1 files changed, 100 insertions(+), 98 deletions(-) diff --git a/poppler/TextOutputDev.cc b/poppler/TextOutputDev.cc index c0599ce..b49fc4d 100644 --- a/poppler/TextOutputDev.cc +++ b/poppler/TextOutputDev.cc @@ -3520,14 +3520,12 @@ void TextSelectionDumper::visitLine (TextLine *line, GooString *TextSelectionDumper::getText (void) { - GBool oneRot = gTrue; GooString *s; TextLineFrag *frag; - int i, col; - GBool multiLine; + int i; UnicodeMap *uMap; - char space[8], eol[16]; - int spaceLen, eolLen; + char eol[16]; + int eolLen; s = new GooString(); @@ -3536,51 +3534,12 @@ GooString *TextSelectionDumper::getText (void) if (uMap == NULL) return s; - spaceLen = uMap->mapUnicode(0x20, space, sizeof(space)); eolLen = uMap->mapUnicode(0x0a, eol, sizeof(eol)); - if (nFrags > 0) { - for (i = 0; i < nFrags; ++i) { - frags[i].computeCoords(oneRot); - } - page->assignColumns(frags, nFrags, oneRot); - - // if all lines in the region have the same rotation, use it; - // otherwise, use the page's primary rotation - if (oneRot) { - qsort(frags, nFrags, sizeof(TextLineFrag), - &TextLineFrag::cmpYXLineRot); - } else { - qsort(frags, nFrags, sizeof(TextLineFrag), - &TextLineFrag::cmpYXPrimaryRot); - } - - col = 0; - multiLine = gFalse; - for (i = 0; i < nFrags; ++i) { - frag = &frags[i]; - - // insert a return - if (frag->col < col || - (i > 0 && fabs(frag->base - frags[i-1].base) > - maxIntraLineDelta * frags[i-1].line->words->fontSize)) { - s->append(eol, eolLen); - col = 0; - multiLine = gTrue; - } - - // column alignment - for (; col < frag->col; ++col) { - s->append(space, spaceLen); - } - - // get the fragment text - col += page->dumpFragment(frag->line->text + frag->start, frag->len, uMap, s); - } - - if (multiLine) { - s->append(eol, eolLen); - } + for (i = 0; i < nFrags; ++i) { + frag = &frags[i]; + page->dumpFragment(frag->line->text + frag->start, frag->len, uMap, s); + s->append(eol, eolLen); } uMap->decRefCnt(); @@ -3934,73 +3893,116 @@ void TextPage::visitSelection(TextSelectionVisitor *visitor, PDFRectangle *selection, SelectionStyle style) { - int i, begin, end; PDFRectangle child_selection; double start_x, start_y, stop_x, stop_y; - TextBlock *b; + TextFlow *flow, *start_flow; + TextBlock *blk, *start_block, *stop_block, *last_block; + double blk_dx, blk_dy, last_dx = 0.0, last_dy = 0.0; + + start_flow = NULL; + start_block = NULL; + stop_block = NULL; + last_block = NULL; - begin = nBlocks; - end = 0; start_x = selection->x1; start_y = selection->y1; stop_x = selection->x2; stop_y = selection->y2; - for (i = 0; i < nBlocks; i++) { - b = blocks[i]; - - if (selection->x1 < b->xMax && selection->y1 < b->yMax && - selection->x2 < b->xMax && selection->y2 < b->yMax && i < begin) { - begin = i; - if (selection->y1 < selection->y2) { - start_x = selection->x1; - start_y = selection->y1; - stop_x = selection->x2; - stop_y = selection->y2; - } else { - start_x = selection->x2; - start_y = selection->y2; - stop_x = selection->x1; - stop_y = selection->y1; + for (flow = flows; flow && !stop_block; flow = flow->next) { + for (blk = flow->blocks; blk && !stop_block; blk = blk->next) { + if (!start_block) { + if (selection->x1 < blk->xMax && + selection->y1 < blk->yMax && + (selection->x2 >= blk->xMax || + selection->y2 >= blk->yMax || + selection->y1 < selection->y2)) { + start_block = blk; + start_flow = flow; + } else if (selection->x2 < blk->xMax && + selection->y2 < blk->yMax) { + start_x = selection->x2; + start_y = selection->y2; + stop_x = selection->x1; + stop_y = selection->y1; + start_block = blk; + start_flow = flow; + } + } + if (start_block) { + if (stop_y > blk->yMin) { + // select a 'good' stop block to use if the stop + // point lies outside a block. + if (flow->xMin < stop_x && + stop_x < flow->xMax) { + // selecting the nearest column can be fooled + // by narrow column titles several paragraphs above. + // allowing any block above the stop point to win + // gives a more natural result. Allow a little fuzz + // or it gets difficult to select the rightmost char + // in a short paragraph. + blk_dx = 0.0; + } else { + blk_dx = fmin(fabs(flow->xMin - stop_x), + fabs(flow->xMax - stop_x)); + } + blk_dy = stop_y - blk->yMin; + // prefer a block from the nearest column; + // and of those, prefer the one above the stop + // point whose top is closest to the stop point. + // allow a little fuzz here, some blocks jut + // out slightl. + if (!last_block || + last_dx - blk_dx > 0.1 || + (fabs(last_dx - blk_dx) < 0.1 && blk_dy < last_dy)) { + last_block = blk; + last_dx = blk_dx; + last_dy = blk_dy; + } + } + if (stop_x > blk->xMin && + stop_y > blk->yMin && + stop_x < blk->xMax && + stop_y < blk->yMax) { + stop_block = blk; + } } - } else if (selection->x1 < b->xMax && selection->y1 < b->yMax && i < begin) { - begin = i; - start_x = selection->x1; - start_y = selection->y1; - stop_x = selection->x2; - stop_y = selection->y2; - } else if (selection->x2 < b->xMax && selection->y2 < b->yMax && i < begin) { - begin = i; - start_x = selection->x2; - start_y = selection->y2; - stop_x = selection->x1; - stop_y = selection->y1; } - - if ((selection->x1 > b->xMin && selection->y1 > b->yMin) || - (selection->x2 > b->xMin && selection->y2 > b->yMin)) - end = i + 1; } - - for (i = begin; i < end; i++) { - if (blocks[i]->xMin < start_x && start_x < blocks[i]->xMax && - blocks[i]->yMin < start_y && start_y < blocks[i]->yMax) { - child_selection.x1 = start_x; - child_selection.y1 = start_y; + if (!start_block) { + return; + } + if (stop_block) { + last_block = stop_block; + } + for (flow = start_flow; flow; flow = flow->next) { + if (flow == start_flow) { + blk = start_block; } else { + blk = flow->blocks; + } + for (; blk; blk = blk->next) { child_selection.x1 = 0; child_selection.y1 = 0; - } - if (blocks[i]->xMin < stop_x && stop_x < blocks[i]->xMax && - blocks[i]->yMin < stop_y && stop_y < blocks[i]->yMax) { - child_selection.x2 = stop_x; - child_selection.y2 = stop_y; - } else { child_selection.x2 = pageWidth; child_selection.y2 = pageHeight; + if (blk == start_block) { + child_selection.x1 = start_x; + child_selection.y1 = start_y; + } + if (blk == last_block) { + if (blk == stop_block) { + // the selection is only cut off in the stop block: + // without this check it becomes hard to select to + // the end of narrow final paragraphs. + child_selection.x2 = stop_x; + child_selection.y2 = stop_y; + } + blk->visitSelection(visitor, &child_selection, style); + return; + } + blk->visitSelection(visitor, &child_selection, style); } - - blocks[i]->visitSelection(visitor, &child_selection, style); } } -- 1.6.2.2