📄 qtextdocumentfragment.cpp
字号:
When inserting such a fragment into a QTextDocument the current char format of the QTextCursor used for insertion is used as format for the text.*/QTextDocumentFragment QTextDocumentFragment::fromPlainText(const QString &plainText){ QTextDocumentFragment res; res.d = new QTextDocumentFragmentPrivate; res.d->importedFromPlainText = true; QTextCursor cursor(res.d->doc); cursor.insertText(plainText); return res;}QTextHtmlImporter::QTextHtmlImporter(QTextDocument *_doc, const QString &_html, ImportMode mode, const QTextDocument *resourceProvider) : indent(0), doc(_doc), importMode(mode){ cursor = QTextCursor(doc); compressNextWhitespace = false; wsm = QTextHtmlParserNode::WhiteSpaceNormal; QString html = _html; const int startFragmentPos = html.indexOf(QLatin1String("<!--StartFragment-->")); if (startFragmentPos != -1) { const int endFragmentPos = html.indexOf(QLatin1String("<!--EndFragment-->")); if (startFragmentPos < endFragmentPos) html = html.mid(startFragmentPos, endFragmentPos - startFragmentPos); else html = html.mid(startFragmentPos); html.prepend(QLatin1String("<meta name=\"qrichtext\" content=\"1\" />")); } parse(html, resourceProvider ? resourceProvider : doc);// dumpHtml();}static QTextListFormat::Style nextListStyle(QTextListFormat::Style style){ if (style == QTextListFormat::ListDisc) return QTextListFormat::ListCircle; else if (style == QTextListFormat::ListCircle) return QTextListFormat::ListSquare; return style;}void QTextHtmlImporter::import(){ cursor.beginEditBlock(); hasBlock = true; forceBlockMerging = false; compressNextWhitespace = !textEditMode; blockTagClosed = false; for (currentNodeIdx = 0; currentNodeIdx < count(); ++currentNodeIdx) { currentNode = &at(currentNodeIdx); wsm = textEditMode ? QTextHtmlParserNode::WhiteSpacePreWrap : currentNode->wsm; /* * process each node in three stages: * 1) check if the hierarchy changed and we therefore passed the * equivalent of a closing tag -> we may need to finish off * some structures like tables * * 2) check if the current node is a special node like a * <table>, <ul> or <img> tag that requires special processing * * 3) if the node should result in a QTextBlock create one and * finally insert text that may be attached to the node */ /* emit 'closing' table blocks or adjust current indent level * if we * 1) are beyond the first node * 2) the current node not being a child of the previous node * means there was a tag closing in the input html */ if (currentNodeIdx > 0 && (currentNode->parent != currentNodeIdx - 1)) { blockTagClosed = closeTag(); // visually collapse subsequent block tags, but if the element after the closed block tag // is for example an inline element (!isBlock) we have to make sure we start a new paragraph by setting // hasBlock to false. if (blockTagClosed && !currentNode->isBlock() && currentNode->id != Html_unknown) hasBlock = false; } if (currentNode->displayMode == QTextHtmlElement::DisplayNone) { if (currentNode->id == Html_title) doc->setMetaInformation(QTextDocument::DocumentTitle, currentNode->text); // ignore explicitly 'invisible' elements continue; } if (processSpecialNodes() == ContinueWithNextNode) continue; // make sure there's a block for 'Blah' after <ul><li>foo</ul>Blah if (blockTagClosed && !hasBlock && !currentNode->isBlock() && !currentNode->text.isEmpty() && !currentNode->hasOnlyWhitespace() && currentNode->displayMode == QTextHtmlElement::DisplayInline) { QTextBlockFormat block = currentNode->blockFormat; block.setIndent(indent); appendBlock(block, currentNode->charFormat); hasBlock = true; } if (currentNode->isBlock()) { if (processBlockNode() == ContinueWithNextNode) continue; } if (currentNode->charFormat.isAnchor() && !currentNode->charFormat.anchorName().isEmpty()) { namedAnchors.append(currentNode->charFormat.anchorName()); } if (appendNodeText()) hasBlock = false; // if we actually appended text then we don't // have an empty block anymore } cursor.endEditBlock();}static bool isPreservingWhitespaceMode(QTextHtmlParserNode::WhiteSpaceMode mode){ return mode == QTextHtmlParserNode::WhiteSpacePre || mode == QTextHtmlParserNode::WhiteSpacePreWrap ;}bool QTextHtmlImporter::appendNodeText(){ const int initialCursorPosition = cursor.position(); QTextCharFormat format = currentNode->charFormat; if (isPreservingWhitespaceMode(wsm)) compressNextWhitespace = false; QString text = currentNode->text; QString textToInsert; textToInsert.reserve(text.size()); for (int i = 0; i < text.length(); ++i) { QChar ch = text.at(i); if (ch.isSpace() && ch != QChar::Nbsp && ch != QChar::ParagraphSeparator) { if (compressNextWhitespace) continue; if (wsm == QTextHtmlParserNode::WhiteSpacePre || textEditMode ) { if (ch == QLatin1Char('\n')) { if (textEditMode) continue; } else if (ch == QLatin1Char('\r')) { continue; } } else if (wsm != QTextHtmlParserNode::WhiteSpacePreWrap) { compressNextWhitespace = true; if (wsm == QTextHtmlParserNode::WhiteSpaceNoWrap) ch = QChar::Nbsp; else ch = QLatin1Char(' '); } } else { compressNextWhitespace = false; } if (ch == QLatin1Char('\n') || ch == QChar::ParagraphSeparator) { if (!textToInsert.isEmpty()) { cursor.insertText(textToInsert, format); textToInsert.clear(); } QTextBlockFormat fmt = cursor.blockFormat(); if (fmt.hasProperty(QTextFormat::BlockBottomMargin)) { QTextBlockFormat tmp = fmt; tmp.clearProperty(QTextFormat::BlockBottomMargin); cursor.setBlockFormat(tmp); } fmt.clearProperty(QTextFormat::BlockTopMargin); appendBlock(fmt, cursor.charFormat()); } else { if (!namedAnchors.isEmpty()) { if (!textToInsert.isEmpty()) { cursor.insertText(textToInsert, format); textToInsert.clear(); } format.setAnchor(true); format.setAnchorNames(namedAnchors); cursor.insertText(ch, format); namedAnchors.clear(); format.clearProperty(QTextFormat::IsAnchor); format.clearProperty(QTextFormat::AnchorName); } else { textToInsert += ch; } } } if (!textToInsert.isEmpty()) { cursor.insertText(textToInsert, format); } return cursor.position() != initialCursorPosition;}QTextHtmlImporter::ProcessNodeResult QTextHtmlImporter::processSpecialNodes(){ switch (currentNode->id) { case Html_body: if (currentNode->charFormat.background().style() != Qt::NoBrush) { QTextFrameFormat fmt = doc->rootFrame()->frameFormat(); fmt.setBackground(currentNode->charFormat.background()); doc->rootFrame()->setFrameFormat(fmt); const_cast<QTextHtmlParserNode *>(currentNode)->charFormat.clearProperty(QTextFormat::BackgroundBrush); } break; case Html_ol: case Html_ul: { QTextListFormat::Style style = currentNode->listStyle; if (currentNode->id == Html_ul && !currentNode->hasOwnListStyle && currentNode->parent) { const QTextHtmlParserNode *n = &at(currentNode->parent); while (n) { if (n->id == Html_ul) { style = nextListStyle(currentNode->listStyle); } if (n->parent) n = &at(n->parent); else n = 0; } } QTextListFormat listFmt; listFmt.setStyle(style); ++indent; if (currentNode->hasCssListIndent) listFmt.setIndent(currentNode->cssListIndent); else listFmt.setIndent(indent); List l; l.format = listFmt; l.listNode = currentNodeIdx; lists.append(l); compressNextWhitespace = true; // broken html: <ul>Text here<li>Foo const QString simpl = currentNode->text.simplified(); if (simpl.isEmpty() || simpl.at(0).isSpace()) return ContinueWithNextNode; break; } case Html_table: { Table t = scanTable(currentNodeIdx); tables.append(t); hasBlock = false; return ContinueWithNextNode; } case Html_tr: return ContinueWithNextNode; case Html_img: { QTextImageFormat fmt; fmt.setName(currentNode->imageName); fmt.merge(currentNode->charFormat); if (currentNode->imageWidth >= 0) fmt.setWidth(currentNode->imageWidth); if (currentNode->imageHeight >= 0) fmt.setHeight(currentNode->imageHeight); cursor.insertImage(fmt, QTextFrameFormat::Position(currentNode->cssFloat)); cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor); cursor.mergeCharFormat(currentNode->charFormat); cursor.movePosition(QTextCursor::Right); hasBlock = false; return ContinueWithNextNode; } case Html_hr: { QTextBlockFormat blockFormat = currentNode->blockFormat; blockFormat.setTopMargin(topMargin(currentNodeIdx)); blockFormat.setBottomMargin(bottomMargin(currentNodeIdx)); blockFormat.setProperty(QTextFormat::BlockTrailingHorizontalRulerWidth, currentNode->width); if (hasBlock && importMode == ImportToDocument) cursor.mergeBlockFormat(blockFormat); else appendBlock(blockFormat); hasBlock = false; return ContinueWithNextNode; } default: break; } return ContinueWithCurrentNode;}// returns true if a block tag was closedbool QTextHtmlImporter::closeTag(){ const QTextHtmlParserNode *closedNode = &at(currentNodeIdx - 1); const int endDepth = depth(currentNodeIdx) - 1; int depth = this->depth(currentNodeIdx - 1); bool blockTagClosed = false; while (depth > endDepth) { Table *t = 0; if (!tables.isEmpty()) t = &tables.last(); switch (closedNode->id) { case Html_tr: if (t && !t->isTextFrame) { ++t->currentRow; // for broken html with rowspans but missing tr tags while (!t->currentCell.atEnd() && t->currentCell.row < t->currentRow) ++t->currentCell; } blockTagClosed = true; break; case Html_table: if (!t) break; indent = t->lastIndent; tables.resize(tables.size() - 1); t = 0; if (tables.isEmpty()) { cursor = doc->rootFrame()->lastCursorPosition(); } else { t = &tables.last(); if (t->isTextFrame) cursor = t->frame->lastCursorPosition(); else if (!t->currentCell.atEnd()) cursor = t->currentCell.cell().lastCursorPosition(); } // we don't need an extra block after tables, so we don't // claim to have closed one for the creation of a new one // in import() blockTagClosed = false; compressNextWhitespace = true; break; case Html_th: case Html_td: if (t && !t->isTextFrame) ++t->currentCell; blockTagClosed = true; compressNextWhitespace = true; break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -