void AnnotationSettings::updateFontIfUniform() { bool uniformFontFamily = true; bool uniformSize = true; bool uniformColor = true; QTextDocument *doc = _ui->content->document(); QTextBlock b = doc->firstBlock(); QTextCharFormat fmt1; bool first=true; // Check all character formats in all blocks. If they are the same, // we can reset the font for the wole document. while(b.isValid()) { for(const QTextLayout::FormatRange &fr : b.textFormats()) { if(first) { fmt1 = fr.format; first = false; } else { uniformFontFamily &= fr.format.fontFamily() == fmt1.fontFamily(); uniformSize &= fr.format.fontPointSize() == fmt1.fontPointSize(); uniformColor &= fr.format.foreground() == fmt1.foreground(); } } b = b.next(); } resetContentFont(uniformFontFamily, uniformSize, uniformColor); }
QString DocumentImporter::importScenario(const ImportParameters& _importParameters) const { // // Преобразовать заданный документ в QTextDocument // QTextDocument documentForImport; QFile documentFile(_importParameters.filePath); documentFile.open(QIODevice::ReadOnly); FormatReader* reader = FormatManager::createReader(&documentFile); reader->read(&documentFile, &documentForImport); // // Найти минимальный отступ слева для всех блоков // ЗАЧЕМ: во многих программах (Final Draft, Screeviner) сделано так, что поля // задаются за счёт оступов. Получается что и заглавие сцены и описание действия // имеют отступы. Так вот это и будет минимальным отступом, который не будем считать // int minLeftMargin = 1000; { QTextCursor cursor(&documentForImport); while (!cursor.atEnd()) { if (minLeftMargin > cursor.blockFormat().leftMargin()) { minLeftMargin = cursor.blockFormat().leftMargin(); } cursor.movePosition(QTextCursor::NextBlock); cursor.movePosition(QTextCursor::EndOfBlock); } } // // FIXME: много чего дублируется с ScenarioXml // // // Преобразовать его в xml-строку // QString scenarioXml; QTextCursor cursor(&documentForImport); QXmlStreamWriter writer(&scenarioXml); writer.writeStartDocument(); writer.writeStartElement(NODE_SCENARIO); writer.writeAttribute(ATTRIBUTE_VERSION, "1.0"); // // Для каждого блока текста определяем тип // // ... последний стиль блока ScenarioBlockStyle::Type lastBlockType = ScenarioBlockStyle::Undefined; // ... количество пустых строк int emptyLines = 0; do { cursor.movePosition(QTextCursor::EndOfBlock); // // Если в блоке есть текст // if (!cursor.block().text().simplified().isEmpty()) { // // ... определяем тип // const ScenarioBlockStyle::Type blockType = ::typeForTextCursor(cursor, lastBlockType, emptyLines, minLeftMargin, _importParameters.outline); const QString blockTypeName = ScenarioBlockStyle::typeName(blockType); QString blockText = cursor.block().text().simplified(); // // ... запишем данные в строку // writer.writeStartElement(blockTypeName); // // Если текущий тип "Время и место" и нужно удалить номер сцены, то делаем это // if (blockType == ScenarioBlockStyle::SceneHeading && _importParameters.removeScenesNumbers){ blockText = blockText.toUpper(); QRegularExpressionMatch match = START_FROM_NUMBER_CHECKER.match(blockText); if (match.hasMatch()) { blockText = blockText.mid(match.capturedEnd()); } } // // Выполняем корректировки // blockText = ::clearBlockText(blockType, blockText); // // Пишем текст // writer.writeStartElement(NODE_VALUE); writer.writeCDATA(blockText); writer.writeEndElement(); // // Пишем редакторские комментарии // if (_importParameters.saveReviewMarks) { const QTextBlock currentBlock = cursor.block(); if (!currentBlock.textFormats().isEmpty()) { writer.writeStartElement(NODE_REVIEW_GROUP); foreach (const QTextLayout::FormatRange& range, currentBlock.textFormats()) { // // Всё, кроме стандартного // if (range.format.boolProperty(Docx::IsForeground) || range.format.boolProperty(Docx::IsBackground) || range.format.boolProperty(Docx::IsHighlight) || range.format.boolProperty(Docx::IsComment)) { writer.writeStartElement(NODE_REVIEW); writer.writeAttribute(ATTRIBUTE_REVIEW_FROM, QString::number(range.start)); writer.writeAttribute(ATTRIBUTE_REVIEW_LENGTH, QString::number(range.length)); if (range.format.hasProperty(QTextFormat::ForegroundBrush)) { writer.writeAttribute(ATTRIBUTE_REVIEW_COLOR, range.format.foreground().color().name()); } if (range.format.hasProperty(QTextFormat::BackgroundBrush)) { writer.writeAttribute(ATTRIBUTE_REVIEW_BGCOLOR, range.format.background().color().name()); } writer.writeAttribute(ATTRIBUTE_REVIEW_IS_HIGHLIGHT, range.format.boolProperty(Docx::IsHighlight) ? "true" : "false"); // // ... комментарии // const QStringList comments = range.format.property(Docx::Comments).toStringList(); const QStringList authors = range.format.property(Docx::CommentsAuthors).toStringList(); const QStringList dates = range.format.property(Docx::CommentsDates).toStringList(); for (int commentIndex = 0; commentIndex < comments.size(); ++commentIndex) { writer.writeEmptyElement(NODE_REVIEW_COMMENT); writer.writeAttribute(ATTRIBUTE_REVIEW_COMMENT, comments.at(commentIndex)); writer.writeAttribute(ATTRIBUTE_REVIEW_AUTHOR, authors.at(commentIndex)); writer.writeAttribute(ATTRIBUTE_REVIEW_DATE, dates.at(commentIndex)); } // writer.writeEndElement(); } } writer.writeEndElement(); } } // // ... конец абзаца // writer.writeEndElement(); // // Запомним последний стиль блока и обнулим счётчик пустых строк // lastBlockType = blockType; emptyLines = 0; } // // Если в блоке нет текста, то увеличиваем счётчик пустых строк // else { ++emptyLines; } cursor.movePosition(QTextCursor::NextCharacter); } while (!cursor.atEnd());