bool BookTextView::getHyperlinkInfo(const ZLTextElementRectangle &rectangle, std::string &id, ZLHyperlinkType &type) const { if ((rectangle.Kind != ZLTextElement::WORD_ELEMENT) && (rectangle.Kind != ZLTextElement::IMAGE_ELEMENT)) { return false; } ZLTextWordCursor cursor = textArea().startCursor(); cursor.moveToParagraph(rectangle.ParagraphIndex); cursor.moveToParagraphStart(); ZLTextKind hyperlinkKind = REGULAR; for (int i = 0; i < rectangle.ElementIndex; ++i) { const ZLTextElement &element = cursor.element(); if (element.kind() == ZLTextElement::CONTROL_ELEMENT) { const ZLTextControlEntry &control = ((const ZLTextControlElement&)element).entry(); if (control.isHyperlink()) { hyperlinkKind = control.kind(); id = ((const ZLTextHyperlinkControlEntry&)control).label(); type = ((const ZLTextHyperlinkControlEntry&)control).hyperlinkType(); } else if (!control.isStart() && (control.kind() == hyperlinkKind)) { hyperlinkKind = REGULAR; } } cursor.nextWord(); } return hyperlinkKind != REGULAR; }
bool BookTextView::getHyperlinkId(const ZLTextElementArea &area, std::string &id, bool &isExternal) const { if ((area.Kind != ZLTextElement::WORD_ELEMENT) && (area.Kind != ZLTextElement::IMAGE_ELEMENT)) { return false; } ZLTextWordCursor cursor = startCursor(); cursor.moveToParagraph(area.ParagraphNumber); cursor.moveToParagraphStart(); ZLTextKind hyperlinkKind = REGULAR; for (int i = 0; i < area.TextElementNumber; ++i) { const ZLTextElement &element = cursor.element(); if (element.kind() == ZLTextElement::CONTROL_ELEMENT) { const ZLTextControlEntry &control = ((const ZLTextControlElement&)element).entry(); if (control.isHyperlink()) { hyperlinkKind = control.kind(); id = ((const ZLTextHyperlinkControlEntry&)control).label(); } else if (!control.isStart() && (control.kind() == hyperlinkKind)) { hyperlinkKind = REGULAR; } } cursor.nextWord(); } isExternal = hyperlinkKind == EXTERNAL_HYPERLINK; return hyperlinkKind != REGULAR; }
bool ZLTextArea::_getHyperlinkInfo(const ZLTextElementRectangle &rectangle, std::string &id, std::string &type, ZLTextKind& hyperlinkKind, int& x1, int&y1, int& x2, int& y2) const { if ((rectangle.Kind != ZLTextElement::WORD_ELEMENT) && (rectangle.Kind != ZLTextElement::IMAGE_ELEMENT)) { return false; } ZLTextWordCursor cursor = startCursor(); cursor.moveToParagraph(rectangle.ParagraphIndex); cursor.moveToParagraphStart(); hyperlinkKind = REGULAR; for (int i = 0; i < rectangle.ElementIndex; ++i) { const ZLTextElement &element = cursor.element(); if (element.kind() == ZLTextElement::CONTROL_ELEMENT) { const ZLTextControlEntry &control = ((const ZLTextControlElement&)element).entry(); if (control.isHyperlink()) { hyperlinkKind = control.kind(); id = ((const ZLTextHyperlinkControlEntry&)control).label(); type = ((const ZLTextHyperlinkControlEntry&)control).hyperlinkType(); x1 = rectangle.XStart; y1 = rectangle.YStart; x2 = rectangle.XEnd; y2 = rectangle.YEnd; } else if (!control.isStart() && (control.kind() == hyperlinkKind)) { hyperlinkKind = REGULAR; } } cursor.nextWord(); } return hyperlinkKind != REGULAR; }
// Added by John to enable caller to get text from current visible page. void ZLTextSelectionModel::text(std::string & text, int max) { text.clear(); ZLTextWordCursor start = myView.startCursor(); ZLTextWordCursor end = myView.endCursor(); ZLTextWordCursor cursor = start; while (cursor < end) { if (cursor.isEndOfParagraph()) { cursor.nextParagraph(); continue; } const ZLTextElement &element = cursor.element(); switch (element.kind()) { case ZLTextElement::WORD_ELEMENT: { const ZLTextWord &word = (const ZLTextWord&)element; if (cursor.sameElementAs(end)) { if (start.sameElementAs(end)) { int skip = ZLUnicodeUtil::length(word.Data, start.charIndex()); int length = ZLUnicodeUtil::length(word.Data, end.charIndex()) - skip; text.append(word.Data + skip, length); } else { text.append(word.Data, ZLUnicodeUtil::length(word.Data, end.charIndex())); } } else if (cursor.charIndex() == 0) { text.append(word.Data, word.Size); } else /* cursor == start */ { int skip = ZLUnicodeUtil::length(word.Data, cursor.charIndex()); text.append(word.Data + skip, word.Size - skip); } } break; case ZLTextElement::HSPACE_ELEMENT: case ZLTextElement::NB_HSPACE_ELEMENT: { text += ' '; } break; } cursor.nextWord(); if (text.size() >= max) { return; } } }
void ZLTextView::ViewStyle::applyControls(const ZLTextWordCursor &begin, const ZLTextWordCursor &end) { for (ZLTextWordCursor cursor = begin; !cursor.equalWordNumber(end); cursor.nextWord()) { const ZLTextElement &element = cursor.element(); if (element.kind() == ZLTextElement::CONTROL_ELEMENT) { applyControl((ZLTextControlElement&)element); } else if (element.kind() == ZLTextElement::FORCED_CONTROL_ELEMENT) { applyControl((ZLTextForcedControlElement&)element); } } }
std::string BookTextView::getFirstInternalHyperlinkId(int x0, int y0, int x1, int y1) { std::string id; std::string type; const ZLTextElementArea * area = elementByCoordinates(x0, y0); const ZLTextElementArea * stop_area = elementByCoordinates(x1, y1); if ( !area || ((area->Kind != ZLTextElement::WORD_ELEMENT) && (area->Kind != ZLTextElement::IMAGE_ELEMENT))) { return id; } ZLTextWordCursor cursor = startCursor(); cursor.moveToParagraph(area->ParagraphIndex); int paragraphs = area->ParagraphIndex; int end_paragraphs = stop_area ? stop_area->ParagraphIndex : 0; do { cursor.moveToParagraphStart(); ZLTextKind hyperlinkKind = REGULAR; for ( ; !cursor.isEndOfParagraph();) { const ZLTextElement &element = cursor.element(); if (element.kind() == ZLTextElement::CONTROL_ELEMENT) { const ZLTextControlEntry &control = ((const ZLTextControlElement&)element).entry(); if (control.isHyperlink()) { hyperlinkKind = control.kind(); id = ((const ZLTextHyperlinkControlEntry&)control).label(); type = ((const ZLTextHyperlinkControlEntry&)control).hyperlinkType(); if (type == "internal") { return id; } } else if (!control.isStart() && (control.kind() == hyperlinkKind)) { hyperlinkKind = REGULAR; } } cursor.nextWord(); } } while( ++paragraphs <= end_paragraphs && cursor.nextParagraph()); return std::string(); }
void ZLTextView::prepareTextLine(const ZLTextLineInfo &info, int y) { y = std::min(y + info.Height, topMargin() + textAreaHeight()); myStyle.setTextStyle(info.StartStyle, info.StartBidiLevel); int spaceCounter = info.SpaceCounter; int fullCorrection = 0; const bool endOfParagraph = info.End.isEndOfParagraph(); bool wordOccured = false; int x = lineStartMargin() + info.StartIndent; const int fontSize = myStyle.textStyle()->fontSize(); // TODO: change metrics at font change const ZLTextStyleEntry::Metrics metrics(fontSize, fontSize / 2, viewWidth(), textAreaHeight()); switch (myStyle.textStyle()->alignment()) { case ALIGN_RIGHT: if (!myStyle.baseIsRtl()) { x += metrics.FullWidth - myStyle.textStyle()->lineEndIndent(metrics, myStyle.baseIsRtl()) - info.Width; } break; case ALIGN_LEFT: if (myStyle.baseIsRtl()) { x += metrics.FullWidth - myStyle.textStyle()->lineEndIndent(metrics, myStyle.baseIsRtl()) - info.Width; } break; case ALIGN_LINESTART: break; case ALIGN_CENTER: x += (metrics.FullWidth - myStyle.textStyle()->lineEndIndent(metrics, myStyle.baseIsRtl()) - info.Width) / 2; break; case ALIGN_JUSTIFY: if (!endOfParagraph && (info.End.element().kind() != ZLTextElement::AFTER_PARAGRAPH_ELEMENT)) { fullCorrection = metrics.FullWidth - myStyle.textStyle()->lineEndIndent(metrics, myStyle.baseIsRtl()) - info.Width; } break; case ALIGN_UNDEFINED: break; } const ZLTextParagraphCursor ¶graph = info.RealStart.paragraphCursor(); int paragraphIndex = paragraph.index(); for (ZLTextWordCursor pos = info.RealStart; !pos.equalElementIndex(info.End); pos.nextWord()) { const ZLTextElement &element = paragraph[pos.elementIndex()]; ZLTextElement::Kind kind = element.kind(); int width = myStyle.elementWidth(element, pos.charIndex(), metrics); myStyle.applySingleControl(element); switch (kind) { case ZLTextElement::WORD_ELEMENT: case ZLTextElement::IMAGE_ELEMENT: { const int height = myStyle.elementHeight(element, metrics); const int descent = myStyle.elementDescent(element); const int length = (kind == ZLTextElement::WORD_ELEMENT) ? ((const ZLTextWord&)element).Length - pos.charIndex() : 0; addAreaToTextMap( ZLTextElementArea( paragraphIndex, pos.elementIndex(), pos.charIndex(), length, false, myStyle.textStyle(), kind, x, x + width - 1, y - height + 1, y + descent, myStyle.bidiLevel() ) ); wordOccured = true; break; } case ZLTextElement::CONTROL_ELEMENT: case ZLTextElement::FORCED_CONTROL_ELEMENT: break; case ZLTextElement::HSPACE_ELEMENT: case ZLTextElement::NB_HSPACE_ELEMENT: if (wordOccured && (spaceCounter > 0)) { int correction = fullCorrection / spaceCounter; x += context().spaceWidth() + correction; fullCorrection -= correction; wordOccured = false; --spaceCounter; } break; case ZLTextElement::INDENT_ELEMENT: case ZLTextElement::BEFORE_PARAGRAPH_ELEMENT: case ZLTextElement::AFTER_PARAGRAPH_ELEMENT: case ZLTextElement::EMPTY_LINE_ELEMENT: case ZLTextElement::FIXED_HSPACE_ELEMENT: break; case ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT: //context().setColor(ZLColor(0, 255, 0)); //context().drawLine(visualX(x), y, visualX(x), y - 20); break; case ZLTextElement::END_REVERSED_SEQUENCE_ELEMENT: flushRevertedElements(myStyle.bidiLevel()); //context().setColor(ZLColor(255, 0, 0)); //context().drawLine(visualX(x), y, visualX(x), y - 20); break; } x += width; } if (!endOfParagraph && (info.End.element().kind() == ZLTextElement::WORD_ELEMENT)) { int start = 0; if (info.End.equalElementIndex(info.RealStart)) { start = info.RealStart.charIndex(); } const int len = info.End.charIndex() - start; if (len > 0) { const ZLTextWord &word = (const ZLTextWord&)info.End.element(); ZLUnicodeUtil::Ucs4String ucs4string; ZLUnicodeUtil::utf8ToUcs4(ucs4string, word.Data, word.Size); const bool addHyphenationSign = ucs4string[start + len - 1] != '-'; const int width = myStyle.wordWidth(word, start, len, addHyphenationSign); const int height = myStyle.elementHeight(word, metrics); const int descent = myStyle.elementDescent(word); addAreaToTextMap( ZLTextElementArea( paragraphIndex, info.End.elementIndex(), start, len, addHyphenationSign, myStyle.textStyle(), ZLTextElement::WORD_ELEMENT, x, x + width - 1, y - height + 1, y + descent, word.BidiLevel ) ); } } for (unsigned char i = myStyle.bidiLevel(); i > myStyle.baseBidiLevel(); --i) { flushRevertedElements(i - 1); } }
void ZLTextSelectionModel::createData() const { if (!myTextIsUpToDate && !isEmpty()) { Range r = internalRange(); ZLTextWordCursor start = myArea.startCursor(); start.moveToParagraph(r.first.ParagraphIndex); start.moveTo(r.first.ElementIndex, r.first.CharIndex); ZLTextWordCursor end = myArea.startCursor(); end.moveToParagraph(r.second.ParagraphIndex); end.moveTo(r.second.ElementIndex, r.second.CharIndex); std::set<ZLTextParagraphCursorPtr> pcursors; pcursors.insert(start.paragraphCursorPtr()); ZLTextWordCursor cursor = start; while (cursor < end) { if (cursor.isEndOfParagraph()) { cursor.nextParagraph(); pcursors.insert(cursor.paragraphCursorPtr()); myText.append(ZLibrary::EndOfLine); continue; } const ZLTextElement &element = cursor.element(); switch (element.kind()) { case ZLTextElement::WORD_ELEMENT: { const ZLTextWord &word = (const ZLTextWord&)element; if (cursor.sameElementAs(end)) { if (start.sameElementAs(end)) { int skip = ZLUnicodeUtil::length(word.Data, start.charIndex()); int length = ZLUnicodeUtil::length(word.Data, end.charIndex()) - skip; myText.append(word.Data + skip, length); } else { myText.append(word.Data, ZLUnicodeUtil::length(word.Data, end.charIndex())); } } else if (cursor.charIndex() == 0) { myText.append(word.Data, word.Size); } else /* cursor == start */ { int skip = ZLUnicodeUtil::length(word.Data, cursor.charIndex()); myText.append(word.Data + skip, word.Size - skip); } break; } case ZLTextElement::IMAGE_ELEMENT: if (myImage.isNull()) { myImage = ((const ZLTextImageElement&)element).image(); } break; case ZLTextElement::HSPACE_ELEMENT: case ZLTextElement::NB_HSPACE_ELEMENT: myText += ' '; break; default: break; } cursor.nextWord(); } if ((cursor == end) && !cursor.isEndOfParagraph() && myImage.isNull()) { const ZLTextElement &element = cursor.element(); if (element.kind() == ZLTextElement::IMAGE_ELEMENT) { myImage = ((const ZLTextImageElement&)element).image(); } } myCursors.swap(pcursors); myTextIsUpToDate = true; } }
const std::vector<ZLTextSelectionModel::Range> &ZLTextSelectionModel::ranges() const { if (!myRangeVectorIsUpToDate && !isEmpty()) { Range r = internalRange(); ZLTextWordCursor cursor = myArea.startCursor(); cursor.moveToParagraph(r.first.ParagraphIndex); cursor.moveToParagraphStart(); int startLevel = 0; for (int i = r.first.ElementIndex; i > 0; --i) { switch (cursor.element().kind()) { case ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT: ++startLevel; break; case ZLTextElement::END_REVERSED_SEQUENCE_ELEMENT: --startLevel; break; default: break; } cursor.nextWord(); } cursor.moveToParagraph(r.second.ParagraphIndex); cursor.moveToParagraphEnd(); int endLevel = 0; for (int i = cursor.elementIndex() - r.second.ElementIndex; i > 0; --i) { cursor.previousWord(); switch (cursor.element().kind()) { case ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT: --endLevel; break; case ZLTextElement::END_REVERSED_SEQUENCE_ELEMENT: ++endLevel; break; default: break; } } if ((startLevel == 0) && (endLevel == 0)) { myRanges.push_back(r); } else if (r.first.ParagraphIndex == r.second.ParagraphIndex) { BoundElement leftBound = r.first; BoundElement rightBound; rightBound.Exists = true; rightBound.ParagraphIndex = leftBound.ParagraphIndex; rightBound.CharIndex = 0; cursor.moveTo(r.first.ElementIndex, 0); for (int i = r.first.ElementIndex; i < r.second.ElementIndex; ++i) { ZLTextElement::Kind kind = cursor.element().kind(); if ((kind == ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT) || (kind == ZLTextElement::END_REVERSED_SEQUENCE_ELEMENT)) { rightBound.ElementIndex = i; myRanges.push_back(Range(leftBound, rightBound)); leftBound = rightBound; } cursor.nextWord(); } myRanges.push_back(Range(leftBound, r.second)); } else { BoundElement leftBound = r.first; if (startLevel > 0) { BoundElement rightBound; rightBound.Exists = true; rightBound.ParagraphIndex = leftBound.ParagraphIndex; rightBound.ElementIndex = leftBound.ElementIndex; rightBound.CharIndex = 0; cursor.moveToParagraph(r.first.ParagraphIndex); cursor.moveTo(r.first.ElementIndex, 0); while (startLevel > 0) { switch(cursor.element().kind()) { case ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT: ++startLevel; rightBound.ElementIndex = cursor.elementIndex(); myRanges.push_back(Range(leftBound, rightBound)); leftBound = rightBound; break; case ZLTextElement::END_REVERSED_SEQUENCE_ELEMENT: --startLevel; rightBound.ElementIndex = cursor.elementIndex(); myRanges.push_back(Range(leftBound, rightBound)); leftBound = rightBound; break; default: break; } cursor.nextWord(); } } BoundElement rightBound1 = r.second; if (endLevel > 0) { BoundElement leftBound1; leftBound1.Exists = true; leftBound1.ParagraphIndex = rightBound1.ParagraphIndex; leftBound1.ElementIndex = rightBound1.ElementIndex; leftBound1.CharIndex = 0; cursor.moveToParagraph(r.second.ParagraphIndex); cursor.moveTo(r.second.ElementIndex, 0); while (endLevel > 0) { switch(cursor.element().kind()) { case ZLTextElement::START_REVERSED_SEQUENCE_ELEMENT: --endLevel; leftBound1.ElementIndex = cursor.elementIndex(); myRanges.push_back(Range(leftBound1, rightBound1)); rightBound1 = leftBound1; break; case ZLTextElement::END_REVERSED_SEQUENCE_ELEMENT: ++endLevel; rightBound1.ElementIndex = cursor.elementIndex(); myRanges.push_back(Range(leftBound1, rightBound1)); rightBound1 = leftBound1; break; default: break; } cursor.previousWord(); } } myRanges.push_back(Range(leftBound, rightBound1)); } myRangeVectorIsUpToDate = true; } return myRanges; }
ZLTextLineInfoPtr ZLTextView::processTextLine(const ZLTextWordCursor &start, const ZLTextWordCursor &end) { const bool useHyphenator = ZLTextStyleCollection::instance().baseStyle().AutoHyphenationOption.value(); ZLTextLineInfoPtr infoPtr = new ZLTextLineInfo(start, myStyle.textStyle()); std::set<ZLTextLineInfoPtr>::const_iterator it = myLineInfoCache.find(infoPtr); if (it != myLineInfoCache.end()) { const ZLTextLineInfoPtr &storedInfo = *it; myStyle.applyControls(storedInfo->Start, storedInfo->End); return storedInfo; } ZLTextLineInfo &info = *infoPtr; ZLTextWordCursor current = start; const ZLTextParagraphCursor ¶graphCursor = current.paragraphCursor(); const bool isFirstLine = current.isStartOfParagraph(); if (paragraphCursor.paragraph().kind() == ZLTextParagraph::TREE_PARAGRAPH) { info.NodeInfo = new ZLTextTreeNodeInfo(); ZLTextTreeNodeInfo &nodeInfo = *info.NodeInfo; const ZLTextTreeParagraph &treeParagraph = (const ZLTextTreeParagraph&)paragraphCursor.paragraph(); nodeInfo.IsLeaf = treeParagraph.children().empty(); nodeInfo.IsOpen = treeParagraph.isOpen(); nodeInfo.IsFirstLine = isFirstLine; nodeInfo.ParagraphNumber = paragraphCursor.index(); nodeInfo.VerticalLinesStack.reserve(treeParagraph.depth() - 1); if (treeParagraph.depth() > 1) { const ZLTextTreeParagraph *ctp = treeParagraph.parent(); nodeInfo.VerticalLinesStack.push_back(ctp->children().back() != &treeParagraph); for (int i = 1; i < treeParagraph.depth() - 1; ++i) { const ZLTextTreeParagraph *parent = ctp->parent(); nodeInfo.VerticalLinesStack.push_back(ctp != parent->children().back()); ctp = parent; } } } if (isFirstLine) { ZLTextElement::Kind elementKind = paragraphCursor[current.wordNumber()].kind(); while ((elementKind == ZLTextElement::CONTROL_ELEMENT) || (elementKind == ZLTextElement::FORCED_CONTROL_ELEMENT)) { const ZLTextElement &element = paragraphCursor[current.wordNumber()]; switch (elementKind) { case ZLTextElement::CONTROL_ELEMENT: myStyle.applyControl((const ZLTextControlElement&)element); break; case ZLTextElement::FORCED_CONTROL_ELEMENT: myStyle.applyControl((const ZLTextForcedControlElement&)element); break; default: break; } current.nextWord(); if (current.equalWordNumber(end)) { break; } elementKind = paragraphCursor[current.wordNumber()].kind(); } info.StartStyle = myStyle.textStyle(); info.RealStart = current; } ZLTextStylePtr storedStyle = myStyle.textStyle(); info.LeftIndent = myStyle.textStyle()->leftIndent(); if (isFirstLine) { info.LeftIndent += myStyle.textStyle()->firstLineIndentDelta(); } if (!info.NodeInfo.isNull()) { info.LeftIndent += (myStyle.context().stringHeight() + 2) / 3 * 4 * (info.NodeInfo->VerticalLinesStack.size() + 1); } info.Width = info.LeftIndent; if (info.RealStart.equalWordNumber(end)) { info.End = info.RealStart; return infoPtr; } ZLTextPartialInfo newInfo(info, current); bool allowBreakAtNBSpace = true; const int maxWidth = viewWidth() - myStyle.textStyle()->rightIndent(); bool wordOccured = false; int lastSpaceWidth = 0; int removeLastSpace = false; ZLTextElement::Kind elementKind = paragraphCursor[newInfo.End.wordNumber()].kind(); bool breakedAtFirstWord = false; do { const ZLTextElement &element = paragraphCursor[newInfo.End.wordNumber()]; newInfo.Width += myStyle.elementWidth(element, newInfo.End.charNumber()); newInfo.Height = std::max(newInfo.Height, myStyle.elementHeight(element)); newInfo.Descent = std::max(newInfo.Descent, myStyle.elementDescent(element)); switch (elementKind) { case ZLTextElement::CONTROL_ELEMENT: myStyle.applyControl((const ZLTextControlElement&)element); break; case ZLTextElement::FORCED_CONTROL_ELEMENT: myStyle.applyControl((const ZLTextForcedControlElement&)element); break; case ZLTextElement::WORD_ELEMENT: case ZLTextElement::IMAGE_ELEMENT: wordOccured = true; newInfo.IsVisible = true; break; case ZLTextElement::HSPACE_ELEMENT: case ZLTextElement::NB_HSPACE_ELEMENT: if (wordOccured) { wordOccured = false; ++newInfo.SpaceCounter; lastSpaceWidth = myStyle.context().spaceWidth(); newInfo.Width += lastSpaceWidth; } break; case ZLTextElement::EMPTY_LINE_ELEMENT: newInfo.IsVisible = true; default: break; } if (newInfo.Width > maxWidth) { if (!info.End.equalWordNumber(start)) { break; } if (useHyphenator && myStyle.textStyle()->allowHyphenations() && (elementKind == ZLTextElement::WORD_ELEMENT)) { breakedAtFirstWord = true; break; } } ZLTextElement::Kind previousKind = elementKind; newInfo.End.nextWord(); bool allowBreak = newInfo.End.equalWordNumber(end); bool nbspaceBreak = false; if (!allowBreak) { elementKind = paragraphCursor[newInfo.End.wordNumber()].kind(); if (elementKind == ZLTextElement::NB_HSPACE_ELEMENT) { if (allowBreakAtNBSpace) { allowBreak = true; nbspaceBreak = true; } } else { allowBreak = ((elementKind != ZLTextElement::WORD_ELEMENT) || (previousKind == ZLTextElement::WORD_ELEMENT)) && (elementKind != ZLTextElement::IMAGE_ELEMENT) && (elementKind != ZLTextElement::CONTROL_ELEMENT); } } if (allowBreak) { newInfo.setTo(info); allowBreakAtNBSpace = nbspaceBreak; storedStyle = myStyle.textStyle(); removeLastSpace = !wordOccured && (info.SpaceCounter > 0); } } while (!newInfo.End.equalWordNumber(end)); if (!newInfo.End.equalWordNumber(end) && useHyphenator && myStyle.textStyle()->allowHyphenations()) { const ZLTextElement &element = paragraphCursor[newInfo.End.wordNumber()]; if (element.kind() == ZLTextElement::WORD_ELEMENT) { const int startCharNumber = newInfo.End.charNumber(); newInfo.Width -= myStyle.elementWidth(element, startCharNumber); const ZLTextWord &word = (ZLTextWord&)element; int spaceLeft = maxWidth - newInfo.Width; if (breakedAtFirstWord || ((word.Length > 3) && (spaceLeft > 2 * myStyle.context().spaceWidth()))) { ZLUnicodeUtil::Ucs2String ucs2string; ZLUnicodeUtil::utf8ToUcs2(ucs2string, word.Data, word.Size); ZLTextHyphenationInfo hyphenationInfo = ZLTextHyphenator::instance().info(word); int hyphenationPosition = word.Length - 1; int subwordWidth = 0; for (; hyphenationPosition > startCharNumber; --hyphenationPosition) { if (hyphenationInfo.isHyphenationPossible(hyphenationPosition)) { subwordWidth = myStyle.wordWidth(word, startCharNumber, hyphenationPosition - startCharNumber, ucs2string[hyphenationPosition - 1] != '-'); if (subwordWidth <= spaceLeft) { break; } } } if ((hyphenationPosition == startCharNumber) && (info.End.wordNumber() <= info.RealStart.wordNumber())) { hyphenationPosition = word.Length; subwordWidth = myStyle.elementWidth(element, startCharNumber); } if (hyphenationPosition > startCharNumber) { newInfo.Width += subwordWidth; newInfo.setTo(info); storedStyle = myStyle.textStyle(); removeLastSpace = false; info.End.setCharNumber(hyphenationPosition); } } } } if (removeLastSpace) { info.Width -= lastSpaceWidth; --info.SpaceCounter; } myStyle.setTextStyle(storedStyle); if (isFirstLine) { info.Height += info.StartStyle->spaceBefore(); } if (info.End.isEndOfParagraph()) { info.VSpaceAfter = myStyle.textStyle()->spaceAfter(); } if (!info.End.equalWordNumber(end) || end.isEndOfParagraph()) { myLineInfoCache.insert(infoPtr); } return infoPtr; }