void RGraphicsSceneQt::exportRectangle(const RVector& p1, const RVector& p2) { if (getEntity() == NULL && !exportToPreview) { qWarning("RGraphicsSceneQt::exportRectangle: entity is NULL"); return; } RPainterPath p; p.setZLevel(0); p.setPen(currentPen); p.setBrush(currentBrush); RVector v = RVector::getMinimum(p1, p2); p.addRect(v.x, v.y, fabs(p2.x - p1.x), fabs(p2.y - p1.y)); if (!exportToPreview) { if (draftMode) { addPath(getBlockRefOrEntity()->getId(), p, true); } else { addPath(getBlockRefOrEntity()->getId(), p, false); } } else { addToPreview(p); } }
void RExporter::exportSplineSegment(const RSpline& spline) { RPainterPath pp; pp.setPen(currentPen); pp.setInheritPen(true); pp.addSpline(spline); exportPainterPaths(QList<RPainterPath>() << pp); }
RPainterPath RPatternLine::getPainterPath() const { RPainterPath p; RVector cursor(0,0); p.moveTo(cursor); // continuous: if (dashes.length()==0) { cursor += RVector::createPolar(10.0, angle); p.lineTo(cursor); } else { for (int i=0; i<dashes.length(); i++) { double dash = dashes[i]; cursor += RVector::createPolar(qAbs(dash), angle); if (dash<0) { p.moveTo(cursor); } else { p.lineTo(cursor); } } } return p; }
RVector RTextBasedData::getPointOnEntity() const { if (painterPaths.size()==0) { return RVector::invalid; } RPainterPath path = painterPaths.at(0); QPointF p = path.pointAtPercent(0.0); return RVector(p.x(), p.y()); }
void RExporter::exportSpline(const RSpline& spline, double offset) { RLinetypePattern p = getLinetypePattern(); bool continuous = false; if (getEntity() == NULL || !p.isValid() || p.getNumDashes() == 1 || draftMode || screenBasedLinetypes) { continuous = true; } if (!continuous) { p.scale(getPatternFactor()); if (RMath::isNaN(offset)) { double length = spline.getLength(); offset = getPatternOffset(length, p); } exportExplodable(spline, offset); } else { // version <= 3.0.0 was (line interpolation): //exportExplodable(spline, offset); // performance improvement (using real splines): RPainterPath pp; pp.setPen(QPen(Qt::SolidLine)); pp.addSpline(spline); exportPainterPaths(QList<RPainterPath>() << pp); } /* RLinetypePattern p = getLinetypePattern(); p.scale(getPatternFactor()); if (RMath::isNaN(offset)) { double length = spline.getLength(); offset = getPatternOffset(length, p); } double currentOffset = offset; QList<QSharedPointer<RShape> > sub = spline.getExploded(); QList<QSharedPointer<RShape> >::iterator it; for (it=sub.begin(); it!=sub.end(); ++it) { QSharedPointer<RLine> line = (*it).dynamicCast<RLine>(); if (!line.isNull()) { exportLine(*line.data(), currentOffset); currentOffset -= line->getLength(); } QSharedPointer<RArc> arc = (*it).dynamicCast<RArc>(); if (!arc.isNull()) { exportArc(*arc.data(), currentOffset); currentOffset -= arc->getLength(); } } */ }
QBrush RExporter::getBrush(const RPainterPath& path) { if (path.isFixedBrushColor()) { // brush is fixed color (text color given): QBrush brush = currentBrush; QColor color = path.getBrush().color(); // color fixed to "by layer": if (color==RColor::CompatByLayer) { if (currentLayer!=NULL) { color = currentLayer->getColor(); } else { qWarning("color by layer but current layer is NULL"); Q_ASSERT(false); } } // color fixed to "by block" (which really means by block reference): if (color==RColor::CompatByBlock) { if (!blockRefStack.isEmpty()) { QStack<REntity*> newBlockRefStack; newBlockRefStack = blockRefStack; newBlockRefStack.pop(); color = blockRefStack.top()->getColor(true, newBlockRefStack); } else { // this can happen (by block at top level): color = RColor(Qt::white); //qWarning("color by block but current block reference is NULL"); //Q_ASSERT(false); } } REntity* e=getEntity(); if (e!=NULL && e->isSelected()) { brush.setColor(RSettings::getSelectionColor()); } else { brush.setColor(color); } return brush; } else { // brush is current brush or no brush: if (path.getBrush().style()!=Qt::NoBrush) { // brush is current brush: return currentBrush; } else { return path.getBrush(); } } }
void RGraphicsSceneQt::exportTriangle(const RTriangle& triangle) { if (getEntity() == NULL && !exportToPreview) { qWarning("RGraphicsSceneQt::exportTriangle: entity is NULL"); return; } // add new painter path with current entity ID: RPainterPath p; p.setZLevel(0); if (draftMode || screenBasedLinetypes) { QPen draftPen = currentPen; draftPen.setWidth(0); p.setPen(draftPen); } else { p.setPen(currentPen); } p.setBrush(currentBrush); p.moveTo(triangle.corner[0]); p.lineTo(triangle.corner[1]); p.lineTo(triangle.corner[2]); p.lineTo(triangle.corner[0]); if (!exportToPreview) { addPath(getBlockRefOrEntity()->getId(), p, draftMode); } else { addToPreview(p); } }
bool RGraphicsView::isPathVisible(const RPainterPath &path) const { double featureSize = path.getFeatureSize(); // no feature size given, always visible: if (fabs(featureSize)<RS::PointTolerance) { return true; } int featureSizePx = (int)mapDistanceToView(fabs(featureSize)); if (featureSize>RS::PointTolerance) { // paths feature size is too small to be displayed (display real text): if (!isPrinting() && featureSizePx<=textHeightThreshold) { return false; } } else if (featureSize<-RS::PointTolerance) { // paths feature size is too large to be displayed (bounding box): if (isPrinting() || featureSizePx>textHeightThreshold) { return false; } } return true; }
/** * \return A QPainterPath object that represents this polyline. */ RPainterPath RPolyline::toPainterPath() const { RPainterPath ret; if (vertices.size()<=1) { return ret; } ret.moveTo(vertices.at(0)); for (int i=0; i<vertices.size(); i++) { if (!closed && i==vertices.size()-1) { break; } QSharedPointer<RShape> shape = getSegmentAt(i); ret.addShape(shape); } return ret; }
void RGraphicsSceneQt::exportImage(const RImageData& image, bool forceSelected) { if (exportToPreview) { RPainterPath path; path.setPen(currentPen); path.setBrush(Qt::NoBrush); if (forceSelected) { path.setSelected(true); } QList<RLine> edges = image.getEdges(); for (int i=0; i<=edges.count(); i++) { if (i==0) { path.moveTo(edges.at(i).getStartPoint()); } else { path.lineTo(edges.at(i % edges.count()).getStartPoint()); } } addToPreview(path); } else { if (images.contains(getBlockRefOrEntity()->getId())) { images[getBlockRefOrEntity()->getId()].append(image); } else { images.insert(getBlockRefOrEntity()->getId(), QList<RImageData>() << image); } } }
void RGraphicsSceneQt::exportPainterPaths(const QList<RPainterPath>& paths) { if (getEntity() == NULL && !exportToPreview) { qWarning("RGraphicsSceneQt::exportPainterPaths: entity is NULL"); return; } RPainterPath path; for (int i=0; i<paths.size(); i++) { path = paths.at(i); path.setZLevel(0); path.setBrush(getBrush(path)); if (path.getInheritPen()) { path.setPen(currentPainterPath.getPen()); } else { path.setPen(getPen(path)); } if (!exportToPreview) { // export into current path (used for complex linetypes): if (currentPainterPath.isValid()) { currentPainterPath.addPath(path); } else { addPath(getBlockRefOrEntity()->getId(), path, draftMode); } } else { addToPreview(path); } } }
QPen RExporter::getPen(const RPainterPath& path) { QPen pen = currentPen; if (draftMode || screenBasedLinetypes) { pen.setWidth(0); } if (path.isFixedPenColor()) { // pen is fixed color (text color given): pen.setColor(path.getPen().color()); return pen; } else { // pen is current pen or no pen: if (path.getPen().style()!=Qt::NoPen) { // pen is current pen: return pen; } return QPen(Qt::NoPen); } }
/** * Updates the internal bounding box. */ void RSpline::updateBoundingBox() const { //getExploded(); RPainterPath pp; pp.addSpline(*this); boundingBox = pp.getBoundingBox(); }
void RExporter::exportSpline(const RSpline& spline, double offset) { RLinetypePattern p = getLinetypePattern(); bool continuous = false; if (getEntity() == NULL || !p.isValid() || p.getNumDashes() <= 1 || draftMode || screenBasedLinetypes || twoColorSelectedMode) { continuous = true; } p.scale(getLineTypePatternScale(p)); double patternLength = p.getPatternLength(); if (patternLength<RS::PointTolerance || spline.getLength() / patternLength > RSettings::getDashThreshold()) { continuous = true; } if (!continuous) { if (getEntity()!=NULL && (getEntity()->getType()!=RS::EntitySpline || RSpline::hasProxy())) { // we have a spline proxy: RShapesExporter(*this, QList<QSharedPointer<RShape> >() << QSharedPointer<RShape>(spline.clone()), offset); } else { // fallback if we don't have a spline proxy: p.scale(getLineTypePatternScale(p)); if (RMath::isNaN(offset)) { double length = spline.getLength(); offset = p.getPatternOffset(length); } exportExplodable(spline, offset); } } else { // version <= 3.0.0 was (line interpolation): //exportExplodable(spline, offset); // performance improvement (using real splines): RPainterPath pp; pp.setPen(currentPen); pp.setInheritPen(true); pp.addSpline(spline); exportPainterPaths(QList<RPainterPath>() << pp); } /* RLinetypePattern p = getLinetypePattern(); p.scale(getLineTypePatternScale(p)); if (RMath::isNaN(offset)) { double length = spline.getLength(); offset = p.getPatternOffset(length); } double currentOffset = offset; QList<QSharedPointer<RShape> > sub = spline.getExploded(); QList<QSharedPointer<RShape> >::iterator it; for (it=sub.begin(); it!=sub.end(); ++it) { QSharedPointer<RLine> line = (*it).dynamicCast<RLine>(); if (!line.isNull()) { exportLine(*line.data(), currentOffset); currentOffset -= line->getLength(); } QSharedPointer<RArc> arc = (*it).dynamicCast<RArc>(); if (!arc.isNull()) { exportArc(*arc.data(), currentOffset); currentOffset -= arc->getLength(); } } */ }
/** * Renders the text data into painter paths. */ void RTextRenderer::render() { boundingBox = RBox(); painterPaths.clear(); richText = ""; QString text = textData.getEscapedText(); //RVector position = textData.getPosition(); RVector position = textData.getAlignmentPoint(); double textHeight = textData.getTextHeight(); RS::VAlign verticalAlignment = textData.getVAlign(); RS::HAlign horizontalAlignment = textData.getHAlign(); double lineSpacingFactor = textData.getLineSpacingFactor(); QString fontName = textData.getFontName(); bool bold = textData.isBold(); bool italic = textData.isItalic(); double angle = textData.getAngle(); //RColor color = textData.getColor(true); // split up text where separation is required due to line feed, // or any type of height change (\P, \H[0-9]*.?[0-9]+;, \S*/*;) // split up text at every formatting change: QRegExp regExpAll(rxAll); QStringList literals = text.split(regExpAll); // collect formatting change information for every literal: QStringList formattings; int pos = 0; while ((pos = regExpAll.indexIn(text, pos)) != -1) { formattings << regExpAll.cap(1); pos += regExpAll.matchedLength(); } // x position in real units: double xCursor = 0.0; // y position for next text line: double yCursor = 0.0; // painter paths for current text line: QList<RPainterPath> linePaths; // max ascent of current text line: double maxAscent = 0.0; // max descent of current line: double minDescent = 0.0; // max descent of _previous_ line: double minDescentPrevious = 0.0; // true for the first block of the current line: bool firstBlockInLine = true; // current line has leading spaces: bool leadingSpaces = false; // current line has trailing spaces: bool trailingSpaces = false; // text has leading empty lines: bool leadingEmptyLines = false; // text has trailing empty lines: bool trailingEmptyLines = false; int lineCounter = 0; QString textBlock; QList<QTextLayout::FormatRange> formats; bool blockChangedHeightOrFont = false; // implicit top level format block: QTextCharFormat f; f.setForeground(QBrush(QColor())); currentFormat.push(f); QTextLayout::FormatRange fr; fr.start = 0; fr.length = 0; fr.format = currentFormat.top(); formats.append(fr); blockHeight.push(textHeight); blockFont.push(fontName); blockBold.push(bold); blockItalic.push(italic); useCadFont.push(RFontList::isCadFont(fontName)); openTags.push(QStringList()); width = 0.0; height = 0.0; // iterate through all text blocks: for (int i=0; i<literals.size(); ++i) { // the literal text, e.g. "ABC": QString literal = literals.at(i); // the formatting _after_ the text, e.g. "\C1;" QString formatting; if (i<formattings.size()) { formatting = formattings.at(i); } // qDebug() << "block: " << i; // qDebug() << " literal: " << literal; // qDebug() << " literal length: " << literal.length(); // qDebug() << " formatting: " << formatting; // qDebug() << " font: " << blockFont.top(); // qDebug() << " height: " << blockHeight.top(); // qDebug() << " cad font: " << useCadFont.top(); // qDebug() << " xCursor: " << xCursor; // handle literal: textBlock.append(literal); if (firstBlockInLine) { if (!literal.isEmpty()) { if (literal.at(0).isSpace()) { leadingSpaces = true; } } else { if (formatting=="\\~") { leadingSpaces = true; } } firstBlockInLine = false; } bool lineFeed = false; bool paragraphFeed = false; bool heightChange = false; bool stackedText = false; bool fontChange = false; bool colorChange = false; bool blockEnd = false; // detect formatting that ends the current text block: if (!formatting.isEmpty()) { fontChange = QRegExp(rxFontChangeTtf).exactMatch(formatting) || QRegExp(rxFontChangeCad).exactMatch(formatting); colorChange = QRegExp(rxColorChangeCustom).exactMatch(formatting) || QRegExp(rxColorChangeIndex).exactMatch(formatting); heightChange = QRegExp(rxHeightChange).exactMatch(formatting); stackedText = QRegExp(rxStackedText).exactMatch(formatting); lineFeed = QRegExp(rxLineFeed).exactMatch(formatting); paragraphFeed = QRegExp(rxParagraphFeed).exactMatch(formatting); blockEnd = QRegExp(rxEndBlock).exactMatch(formatting); } bool start = (i==0); bool end = (i==literals.size()-1); // first line is empty: if (textBlock.trimmed().isEmpty() && (lineFeed || paragraphFeed || end)) { if (start) { leadingEmptyLines = true; } else if (end) { trailingEmptyLines = true; } } // reached a new text block that needs to be rendered separately // due to line feed, height change, font change, ...: if (target==RichText || lineFeed || paragraphFeed || heightChange || stackedText || fontChange || colorChange || end || (blockEnd && blockChangedHeightOrFont)) { // render block _before_ format change that requires new block: if (textBlock!="") { // fix format lengths to match up. each format stops where // the next one starts. formatting that is carried over is set // again for the new format. for (int i=0; i<formats.size(); i++) { int nextStart; if (i<formats.size()-1) { nextStart = formats[i+1].start; } else { nextStart = textBlock.length(); } formats[i].length = nextStart - formats[i].start; } if (target==RichText) { richText += getRichTextForBlock( textBlock, formats); } if (target==PainterPaths) { double horizontalAdvance = 0.0; double horizontalAdvanceNoSpacing = 0.0; double ascent = 0.0; double descent = 0.0; QList<RPainterPath> paths; // get painter paths for current text block at height 1.0: paths = getPainterPathsForBlock( textBlock, formats, horizontalAdvance, horizontalAdvanceNoSpacing, ascent, descent); // transform to scale text from 1.0 to current text height: QTransform sizeTransform; sizeTransform.scale(blockHeight.top(), blockHeight.top()); // transform for current block due to xCursor position: QTransform blockTransform; blockTransform.translate(xCursor, 0); // combine transforms for current text block: QTransform allTransforms = sizeTransform; allTransforms *= blockTransform; maxAscent = qMax(maxAscent, ascent * blockHeight.top()); minDescent = qMin(minDescent, descent * blockHeight.top()); // transform paths of current block and append to paths // of current text line: for (int i=0; i<paths.size(); ++i) { RPainterPath p = paths.at(i); p.transform(allTransforms); linePaths.append(p); } xCursor += horizontalAdvance * blockHeight.top(); } } // empty text, might be line feed, we need ascent, descent anyway: else if (lineFeed || paragraphFeed || end) { if (target==PainterPaths) { double horizontalAdvance = 0.0; double horizontalAdvanceNoSpacing = 0.0; double ascent = 0.0; double descent = 0.0; // get painter paths for current text block at height 1.0: getPainterPathsForBlock( "A", QList<QTextLayout::FormatRange>(), horizontalAdvance, horizontalAdvanceNoSpacing, ascent, descent); maxAscent = qMax(maxAscent, ascent * blockHeight.top()); minDescent = qMin(minDescent, descent * blockHeight.top()); } } // handle stacked text: if (stackedText) { QRegExp reg; reg.setPattern(rxStackedText); reg.exactMatch(formatting); if (target==PainterPaths) { double horizontalAdvance[2]; horizontalAdvance[0] = 0.0; horizontalAdvance[1] = 0.0; double horizontalAdvanceNoSpacing[2]; horizontalAdvanceNoSpacing[0] = 0.0; horizontalAdvanceNoSpacing[1] = 0.0; double heightFactor = 0.4; // s==0: superscript, s==1: subscript: for (int s=0; s<=1; ++s) { QString script = reg.cap(s+1); QList<RPainterPath> paths; double ascent = 0.0; double descent = 0.0; QList<QTextLayout::FormatRange> localFormats; QTextLayout::FormatRange fr; fr.start = 0; fr.length = script.length(); fr.format = currentFormat.top(); localFormats.append(fr); // get painter paths for superscript at height 1.0: paths = getPainterPathsForBlock( script, localFormats, horizontalAdvance[s], horizontalAdvanceNoSpacing[s], ascent, descent); if (s==0) { maxAscent = qMax(maxAscent, ascent * blockHeight.top() * heightFactor + blockHeight.top()*(1.0-heightFactor)); } else { minDescent = qMin(minDescent, descent * blockHeight.top() * heightFactor); } // transform to scale text from 1.0 to current text height * 0.4: QTransform sizeTransform; sizeTransform.scale(blockHeight.top()*heightFactor, blockHeight.top()*heightFactor); // move top text more to the right for italic texts: double xOffset = 0.0; if (s==0 && blockItalic.top()==true) { double y = blockHeight.top()*(1.0-heightFactor); // assume italic means roughly 12 degrees: xOffset = tan(RMath::deg2rad(12)) * y; } // transform for current block due to xCursor position // and top or bottom text position: QTransform blockTransform; blockTransform.translate(xCursor + xOffset, s==0 ? blockHeight.top()*(1.0-heightFactor) : 0.0); horizontalAdvance[s] += xOffset / (blockHeight.top() * heightFactor); // combine transforms for current text block: QTransform allTransforms = sizeTransform; allTransforms *= blockTransform; // transform paths of current block and append to paths // of current text line: for (int i=0; i<paths.size(); ++i) { RPainterPath p = paths.at(i); p.transform(allTransforms); linePaths.append(p); } } xCursor += qMax(horizontalAdvance[0], horizontalAdvance[1]) * blockHeight.top() * heightFactor; } if (target==RichText) { QString super = reg.cap(1); QString sub = reg.cap(2); if (!super.isEmpty()) { richText += QString("<span style=\"vertical-align:super;\">%1</span>").arg(super); } if (!sub.isEmpty()) { richText += QString("<span style=\"vertical-align:sub;\">%1</span>").arg(sub); } } } // prepare for next text block: if (lineFeed || paragraphFeed || end) { if (!textBlock.isEmpty()) { if (textBlock.at(textBlock.length()-1).isSpace()) { trailingSpaces = true; } } // else { // if (formatting=="\\~") { // trailingSpaces = true; // } // } } textBlock = ""; formats.clear(); QTextLayout::FormatRange fr; fr.start = 0; fr.length = 0; fr.format = currentFormat.top(); formats.append(fr); // handle text line. // add all painter paths of the current line to result set of // painter paths. apply line feed transformations. if (lineFeed || paragraphFeed || end) { // qDebug() << "lineFeed: adding text line:"; // qDebug() << " maxAscent: " << maxAscent; // qDebug() << " minDescent: " << minDescent; // qDebug() << " minDescentPrevious: " << minDescentPrevious; // qDebug() << "got line feed or end"; // qDebug() << " trailingSpaces: " << trailingSpaces; if (target==RichText) { if (lineFeed || paragraphFeed) { richText += "<br/>"; } } if (lineCounter!=0) { yCursor += (minDescentPrevious - maxAscent) * lineSpacingFactor; } // calculate line bounding box for alignment without spaces at borders: RBox lineBoundingBox; for (int i=0; i<linePaths.size(); ++i) { RPainterPath p = linePaths.at(i); lineBoundingBox.growToInclude(p.getBoundingBox()); } double featureSize = lineBoundingBox.getHeight(); QTransform lineTransform; switch (horizontalAlignment) { case RS::HAlignAlign: case RS::HAlignFit: case RS::HAlignLeft: if (!leadingSpaces) { // move completely to the left (left border is 0.0): lineTransform.translate( -lineBoundingBox.getMinimum().x, yCursor); } else { lineTransform.translate(0, yCursor); } break; case RS::HAlignMid: case RS::HAlignCenter: if (!leadingSpaces && !trailingSpaces) { lineTransform.translate( -(lineBoundingBox.getMinimum().x + lineBoundingBox.getMaximum().x)/2.0, yCursor); } else { lineTransform.translate(-xCursor/2.0, yCursor); } break; case RS::HAlignRight: if (!trailingSpaces) { lineTransform.translate(-lineBoundingBox.getMaximum().x, yCursor); } else { lineTransform.translate(-xCursor, yCursor); } break; } width = qMax(width, lineBoundingBox.getMaximum().x - qMin(0.0, lineBoundingBox.getMinimum().x)); // add current text line to result set: QPen pen; for (int i=0; i<linePaths.size(); ++i) { RPainterPath p = linePaths[i]; if (i==0) { pen = p.getPen(); if (pen.style()==Qt::NoPen) { pen = QPen(p.getBrush().color()); } } p.transform(lineTransform); p.setFeatureSize(featureSize); painterPaths.append(p); boundingBox.growToInclude(p.getBoundingBox()); } RPainterPath bbPath; bbPath.addBox(lineBoundingBox); bbPath.transform(lineTransform); bbPath.setFeatureSize(-featureSize); bbPath.setPen(pen); painterPaths.append(bbPath); lineCounter++; xCursor = 0.0; maxAscent = 0.0; minDescentPrevious = minDescent; minDescent = 0.0; linePaths.clear(); firstBlockInLine = true; leadingSpaces = false; trailingSpaces = false; } } // handle formatting after text block, that applies either // to the next text block(s) or the rest of the current text block: if (formatting.isEmpty()) { continue; } QRegExp reg; // unicode: reg.setPattern(rxUnicode); if (reg.exactMatch(formatting)) { textBlock += QChar(reg.cap(1).toInt(0, 16)); continue; } // curly braket open: reg.setPattern(rxCurlyOpen); if (reg.exactMatch(formatting)) { textBlock += "{"; continue; } // curly braket close: reg.setPattern(rxCurlyClose); if (reg.exactMatch(formatting)) { textBlock += "}"; continue; } // degree: reg.setPattern(rxDegree); if (reg.exactMatch(formatting)) { textBlock += chDegree; continue; } // plus/minus: reg.setPattern(rxPlusMinus); if (reg.exactMatch(formatting)) { textBlock += chPlusMinus; continue; } // diameter: reg.setPattern(rxDiameter); if (reg.exactMatch(formatting)) { textBlock += chDiameter; continue; } // backslash: reg.setPattern(rxBackslash); if (reg.exactMatch(formatting)) { textBlock += "\\"; continue; } // non-breaking space: reg.setPattern(rxNonBreakingSpace); if (reg.exactMatch(formatting)) { if (target==PainterPaths) { textBlock += QChar(Qt::Key_nobreakspace); } if (target==RichText) { textBlock += " "; } continue; } // font change (TTF): reg.setPattern(rxFontChangeTtf); if (reg.exactMatch(formatting)) { blockFont.top() = reg.cap(1); blockBold.top() = (reg.cap(2).toInt()!=0); blockItalic.top() = (reg.cap(3).toInt()!=0); useCadFont.top() = false; blockChangedHeightOrFont = true; if (target==RichText) { QString style; style += QString("font-family:%1;").arg(blockFont.top()); style += QString("font-weight:%1;").arg(blockBold.top() ? "bold" : "normal"); style += QString("font-style:%1;").arg(blockItalic.top() ? "italic" : "normal"); richText += QString("<span style=\"%1\">").arg(style); openTags.top().append("span"); } continue; } // font change (CAD): reg.setPattern(rxFontChangeCad); if (reg.exactMatch(formatting)) { blockFont.top() = reg.cap(1); useCadFont.top() = true; if (xCursor>RS::PointTolerance) { RFont* f = RFontList::get(blockFont.top()); if (f!=NULL && f->isValid()) { xCursor += f->getLetterSpacing() / 9.0; } } blockChangedHeightOrFont = true; if (target==RichText) { QString style; style += QString("font-family:%1;").arg(blockFont.top()); richText += QString("<span style=\"%1\">").arg(style); openTags.top().append("span"); } continue; } // height change: reg.setPattern(rxHeightChange); if (reg.exactMatch(formatting)) { bool factor = reg.cap(2)=="x"; if (factor) { blockHeight.top() *= reg.cap(1).toDouble(); } else { blockHeight.top() = reg.cap(1).toDouble(); } blockChangedHeightOrFont = true; if (target==RichText) { QString style; style += QString("font-size:%1pt;").arg(blockHeight.top() * fontHeightFactor); richText += QString("<span style=\"%1\">").arg(style); openTags.top().append("span"); } continue; } QTextLayout::FormatRange fr; fr.start = textBlock.length(); fr.length = 0; // start format block: reg.setPattern(rxBeginBlock); if (reg.exactMatch(formatting)) { currentFormat.push(currentFormat.top()); blockFont.push(blockFont.top()); blockBold.push(blockBold.top()); blockItalic.push(blockItalic.top()); blockHeight.push(blockHeight.top()); useCadFont.push(useCadFont.top()); if (target==RichText) { openTags.push(QStringList()); } blockChangedHeightOrFont = false; continue; } // end format block: reg.setPattern(rxEndBlock); if (reg.exactMatch(formatting)) { currentFormat.pop(); fr.format = currentFormat.top(); formats.append(fr); blockFont.pop(); blockBold.pop(); blockItalic.pop(); blockHeight.pop(); useCadFont.pop(); blockChangedHeightOrFont = false; if (target==RichText) { // close all tags that were opened in this block: for (int i=openTags.top().size()-1; i>=0; --i) { QString tag = openTags.top().at(i); richText += QString("</%1>").arg(tag); } openTags.pop(); } continue; } // color change (indexed): reg.setPattern(rxColorChangeIndex); if (reg.exactMatch(formatting)) { RColor blockColor = RColor::createFromCadIndex(reg.cap(1)); if (blockColor.isByLayer()) { currentFormat.top().setForeground(RColor::CompatByLayer); } else if (blockColor.isByBlock()) { currentFormat.top().setForeground(RColor::CompatByBlock); } else { currentFormat.top().setForeground(blockColor); } fr.format = currentFormat.top(); formats.append(fr); if (target==RichText) { QString style; style += QString("color:%1;").arg(blockColor.name()); richText += QString("<span style=\"%1\">").arg(style); openTags.top().append("span"); } continue; } // color change (custom): reg.setPattern(rxColorChangeCustom); if (reg.exactMatch(formatting)) { RColor blockColor = RColor::createFromCadCustom(reg.cap(1)); currentFormat.top().setForeground(blockColor); fr.format = currentFormat.top(); formats.append(fr); if (target==RichText) { QString style; style += QString("color:%1;").arg(blockColor.name()); richText += QString("<span style=\"%1\">").arg(style); openTags.top().append("span"); } continue; } } if (target==PainterPaths) { // at this point, the text is at 0/0 with the base line of the // first text line at y==0 // vertical alignment: double topLine = qMax(textHeight, boundingBox.getMaximum().y); double bottomLine = qMin(0.0, boundingBox.getMinimum().y); QTransform globalTransform; globalTransform.translate(position.x, position.y); globalTransform.rotate(RMath::rad2deg(angle)); switch (verticalAlignment) { case RS::VAlignTop: //if (!leadingEmptyLines) { globalTransform.translate(0.0, -topLine); //} break; case RS::VAlignMiddle: if (leadingEmptyLines || trailingEmptyLines) { globalTransform.translate(0.0, -(yCursor+textHeight)/2.0); } else { globalTransform.translate(0.0, -(bottomLine + topLine) / 2.0); } break; case RS::VAlignBottom: case RS::VAlignBase: if (trailingEmptyLines) { globalTransform.translate(0.0, -yCursor); } else { globalTransform.translate(0.0, -bottomLine); } break; } height = boundingBox.getHeight(); // apply global transform for position, angle and vertical alignment: boundingBox = RBox(); for (int i=0; i<painterPaths.size(); ++i) { painterPaths[i].transform(globalTransform); boundingBox.growToInclude(painterPaths[i].getBoundingBox()); } } if (target==RichText) { // close all tags that were opened: for (int i=openTags.top().size()-1; i>=0; --i) { QString tag = openTags.top().at(i); richText += QString("</%1>").arg(tag); } } }
/** * Renders the text data into painter paths. */ void RTextRenderer::renderSimple() { boundingBox = RBox(); painterPaths.clear(); richText = ""; RVector pos = textData.getAlignmentPoint(); QString text = textData.getEscapedText(); double textHeight = textData.getTextHeight(); RS::VAlign verticalAlignment = textData.getVAlign(); RS::HAlign horizontalAlignment = textData.getHAlign(); QString fontName = textData.getFontName(); bool bold = textData.isBold(); bool italic = textData.isItalic(); double angle = textData.getAngle(); // degree: text.replace(QRegExp(RTextRenderer::rxDegree), RTextRenderer::chDegree); // plus minus: text.replace(QRegExp(RTextRenderer::rxPlusMinus), RTextRenderer::chPlusMinus); // diameter: text.replace(QRegExp(RTextRenderer::rxDiameter), RTextRenderer::chDiameter); bool leadingSpaces = false; bool trailingSpaces = false; if (!text.isEmpty()) { if (text.at(0).isSpace()) { leadingSpaces = true; } if (text.at(text.length()-1).isSpace()) { trailingSpaces = true; } } // implicit top level format block: QTextCharFormat f; f.setForeground(QBrush(QColor())); currentFormat.push(f); QTextLayout::FormatRange fr; fr.start = 0; fr.length = text.length(); fr.format = currentFormat.top(); QList<QTextLayout::FormatRange> formats; formats.append(fr); blockHeight.push(textHeight); blockFont.push(fontName); blockBold.push(bold); blockItalic.push(italic); useCadFont.push(RFontList::isCadFont(blockFont.top())); openTags.push(QStringList()); double horizontalAdvance = 0.0; double horizontalAdvanceNoSpacing = 0.0; double ascent = 0.0; double descent = 0.0; // get painter paths for text at height 1.0: painterPaths = getPainterPathsForBlock( text, formats, horizontalAdvance, horizontalAdvanceNoSpacing, ascent, descent); width = horizontalAdvanceNoSpacing * textHeight; // transform to scale text from 1.0 to current text height: QTransform sizeTransform; sizeTransform.scale(textHeight, textHeight); // transform paths of text: boundingBox = RBox(); for (int i=0; i<painterPaths.size(); ++i) { painterPaths[i].transform(sizeTransform); boundingBox.growToInclude(painterPaths[i].getBoundingBox()); } // feature size of a text is its height // determines if text is displayed or only bounding box double featureSize = boundingBox.getHeight(); QPen pen; for (int i=0; i<painterPaths.size(); ++i) { if (i==0) { pen = painterPaths[i].getPen(); if (pen.style()==Qt::NoPen) { pen = QPen(painterPaths[i].getBrush().color()); } } painterPaths[i].setFeatureSize(featureSize); } RPainterPath bbPath; bbPath.addBox(boundingBox); bbPath.setFeatureSize(-featureSize); bbPath.setPen(pen); painterPaths.append(bbPath); //height = boundingBox.getHeight(); double topLine = textHeight; double baseLine = 0.0; double bottomLine = descent * textHeight; // at this point, the text is at 0/0 with the base line of the // first text line at y==0 double xOffset = 0.0; double yOffset = 0.0; switch (verticalAlignment) { case RS::VAlignTop: yOffset = -topLine; break; case RS::VAlignMiddle: yOffset = -textHeight/2.0; break; case RS::VAlignBase: yOffset = -baseLine; break; case RS::VAlignBottom: yOffset = -bottomLine; break; } switch (horizontalAlignment) { default: case RS::HAlignAlign: case RS::HAlignFit: case RS::HAlignLeft: if (!leadingSpaces) { // move completely to the left (left border is 0.0): xOffset = -boundingBox.getMinimum().x; } else { xOffset = 0.0; } break; case RS::HAlignMid: case RS::HAlignCenter: if (!leadingSpaces && !trailingSpaces) { xOffset = -(boundingBox.getMinimum().x + boundingBox.getMaximum().x)/2.0; } else { xOffset = -horizontalAdvance/2.0*textHeight; } break; case RS::HAlignRight: if (!trailingSpaces) { xOffset = -boundingBox.getMaximum().x; } else { xOffset = -horizontalAdvance*textHeight; } break; } height = boundingBox.getHeight(); QTransform globalTransform; globalTransform.translate(pos.x, pos.y); globalTransform.rotate(RMath::rad2deg(angle)); globalTransform.translate(xOffset, yOffset); // apply global transform for position, angle and vertical alignment: boundingBox = RBox(); for (int i=0; i<painterPaths.size(); ++i) { painterPaths[i].transform(globalTransform); boundingBox.growToInclude(painterPaths[i].getBoundingBox()); } }