// // Class: RenderedDocumentPage // // Widget for displaying TeX DVI files. // Part of KDVI- A previewer for TeX DVI files. // // (C) 2004 Stefan Kebekus. Distributed under the GPL. #include #include #include "renderedDocumentPage.h" #include "hyperlink.h" #include "selection.h" #include "textBox.h" RenderedDocumentPage::RenderedDocumentPage() { textBoxList.reserve(250); pageNr = 0; isEmpty = true; pageText = TQString(); } RenderedDocumentPage::~RenderedDocumentPage() { ; } void RenderedDocumentPage::setPageNumber(const PageNumber& pnr) { pageNr = pnr; clear(); } void RenderedDocumentPage::clear() { #ifdef DEBUG_DOCUMENTPAGE kdDebug(1223) << "RenderedDocumentPage::clear() called for page #" << pageNumber << endl; #endif textBoxList.clear(); hyperLinkList.clear(); pageText = TQString(); isEmpty = true; } TQRegion RenderedDocumentPage::selectedRegion(const TextSelection& selection) { if (selection.isEmpty() || selection.getPageNumber() != pageNr) return TQRegion(); int startIndex = selection.getSelectedTextStart(); int endIndex = selection.getSelectedTextEnd(); TQValueVector wordBox; TQRect currentWordBox; //unsigned int currentBaseline = 0; // Merge character boxes into boxes containing complete words. // Note: A word in this context is defined as a string of boxes // with the same baseline. for (int i = startIndex; i <= endIndex; i++) { if (i == 0) { // start first word currentWordBox = textBoxList[i].box; //currentBaseline = textBoxList[i].baseline; continue; } /*if (currentBaseline == textBoxList[i].baseline) { currentWordBox = currentWordBox.unite(textBoxList[i].box); } else*/ { // start next word wordBox.push_back(currentWordBox); currentWordBox = textBoxList[i].box; //currentBaseline = textBoxList[i].baseline; } } // we still need to store the last word wordBox.push_back(currentWordBox); TQValueVector lineBox; // Merge word boxes into boxes containing whole lines. // We start a new line if we encounter a wordbox which does not // vertically overlap which the current lineBox. TQRect currentLineBox; for (unsigned int i = 0; i < wordBox.size(); i++) { if (!currentLineBox.isValid()) { // start first line currentLineBox = wordBox[i]; continue; } // check if there is vertical overlap if (wordBox[i].top() <= currentLineBox.bottom() && wordBox[i].bottom() >= currentLineBox.top()) { // the word belongs to the current line currentLineBox = currentLineBox.unite(wordBox[i]); } else { // start next line //kdDebug() << "push line (" << currentLineBox.top() << ", " << currentLineBox.bottom() << ")" << endl; lineBox.push_back(currentLineBox); currentLineBox = wordBox[i]; } } // we still need to store the last line //kdDebug() << "push line (" << currentLineBox.top() << ", " << currentLineBox.bottom() << ")" << endl; lineBox.push_back(currentLineBox); //kdDebug() << "Number of lineboxes = " << lineBox.size() << endl; // Now we increase the height of the lines if necessary to obtain a connected region // for our selection. for (unsigned int i = 0; i < lineBox.size() - 1; i++) { if (lineBox[i+1].top() >= lineBox[i].bottom()) { int midPoint = (lineBox[i].bottom() + lineBox[i+1].top()) / 2; lineBox[i].setBottom(midPoint); lineBox[i+1].setTop(midPoint+1); } } // Add the lineboxes to a Region TQRegion selectedRegion; for (unsigned int i = 0; i < lineBox.size(); i++) { selectedRegion += TQRegion(lineBox[i]); } return selectedRegion; } TextSelection RenderedDocumentPage::select(const TQRect& selectedRectangle) { int selectedTextStart = -1; int selectedTextEnd = -1; // Find the smallest and biggest index for which the corresponding // textBoxList entry intersects the selected rectangle. for (unsigned int i=0; i::Iterator i = textBoxList.begin(); i != textBoxList.end(); i++) { pageText = pageText + i->text; } } // Create empty selection; TextSelection selection; // If the page contains no searchable text if (pageText.isNull()) return selection; // Compute the corresponding pageText index unsigned int subIndex = 0; for (int i = 0; i < index; i++) { subIndex += textBoxList[i].text.length(); } int textIndex = pageText.find(str, subIndex, caseSensitive); if (textIndex == -1) return selection; // Because a single Hyperlink structure can possible store more then // one character we now have to search for the Indices in the // textBoxList Vector which corresponds to the found index in the // String pageText. FIXME: It would be faster to search directly in // the textBoxList. int counter = 0; int startIndex = 0; while (counter < textIndex) { counter += textBoxList[startIndex].text.length(); // If the string we searched for starts in the middle of some text element we better return a // selection that it somewhat bigger. if (counter > textIndex) break; startIndex++; // safety check if (startIndex >= (int)textBoxList.size()) return selection; } // Search for the end index. // TODO: This algorithm is not entirely correct if str does not start exactly at the beginning of some text element. counter = 0; int endIndex = startIndex; while (counter < (int)str.length()) { counter += textBoxList[endIndex].text.length(); if (counter >= (int)str.length()) break; endIndex++; // safety check if (endIndex >= (int)textBoxList.size()) return selection; } // Set the selection selection.set(pageNr, startIndex, endIndex, str); return selection; } TextSelection RenderedDocumentPage::findRev(const TQString& str, int index, bool caseSensitive) { // Negative index means we start the search at the end of the text. if (index < 0) { index = textBoxList.size(); } if (pageText.isNull()) { // Create the pageText by joining all entries of textBoxList. for (TQValueVector::Iterator i = textBoxList.begin(); i != textBoxList.end(); i++) { pageText = pageText + i->text; } } // Create empty selection; TextSelection selection; // If the page contains no searchable text if (pageText.isNull()) return selection; // Compute the corresponding pageText index unsigned int subIndex = 0; for (int i = 0; i < index; i++) { subIndex += textBoxList[i].text.length(); } int textIndex = pageText.findRev(str, subIndex, caseSensitive); if (textIndex == -1) return selection; // Because a single Hyperlink structure can possible store more then // one character we now have to search for the Indices in the // textBoxList Vector which corresponds to the found index in the // String pageText. FIXME: It would be faster to search directly in // the textBoxList. int counter = 0; int startIndex = 0; while (counter < textIndex) { counter += textBoxList[startIndex].text.length(); // If the string we searched for starts in the middle of some text element we better return a // selection that it somewhat bigger. if (counter > textIndex) break; startIndex++; // safety check if (startIndex >= (int)textBoxList.size()) return selection; } // Search for the end index. // TODO: This algorithm is not entirely correct if str does not start exactly at the beginning of some text element. counter = 0; int endIndex = startIndex; while (counter < (int)str.length()) { counter += textBoxList[endIndex].text.length(); if (counter >= (int)str.length()) break; endIndex++; // safety check if (endIndex >= (int)textBoxList.size()) return selection; } // Set the selection selection.set(pageNr, startIndex, endIndex, str); return selection; } #include "renderedDocumentPage.moc"