diff --git a/cartocrow/core/arrangement_helpers.h b/cartocrow/core/arrangement_helpers.h index 46a8e4a3..18d77232 100644 --- a/cartocrow/core/arrangement_helpers.h +++ b/cartocrow/core/arrangement_helpers.h @@ -130,7 +130,7 @@ PolygonWithHoles face_to_polygon_with_holes(FaceH fh) { /// Constructs a general polygon with holes from a face of an arrangement. /// The Traits class should be a model of ArrangementDirectionalXMonotoneTraits_2. template -CGAL::General_polygon_with_holes_2 face_to_general_polygon_with_holes(FaceH fh) { +CGAL::General_polygon_with_holes_2> face_to_general_polygon_with_holes(FaceH fh) { CGAL::General_polygon_2 outer; if (fh->has_outer_ccb()) { outer = ccb_to_general_polygon(fh->outer_ccb()); diff --git a/cartocrow/renderer/geometry_renderer.h b/cartocrow/renderer/geometry_renderer.h index a35ea135..b7ae83ff 100644 --- a/cartocrow/renderer/geometry_renderer.h +++ b/cartocrow/renderer/geometry_renderer.h @@ -210,6 +210,14 @@ class GeometryRenderer { virtual void setHorizontalTextAlignment(HorizontalTextAlignment alignment) = 0; /// Set vertical text alignment. virtual void setVerticalTextAlignment(VerticalTextAlignment alignment) = 0; + /// Set font family (assumes it is the name of a system font that is installed) + virtual void setFontFamily(std::string fontFamily) = 0; + /// Set font size + virtual void setFontSize(double fontSize) = 0; + /// Set font weight + virtual void setFontWeight(bool bold) = 0; + /// Remove font specification (if possible in the renderer) and use the default instead + virtual void useDefaultFont() = 0; /// @} }; diff --git a/cartocrow/renderer/geometry_widget.cpp b/cartocrow/renderer/geometry_widget.cpp index 0679579d..c3151bf8 100644 --- a/cartocrow/renderer/geometry_widget.cpp +++ b/cartocrow/renderer/geometry_widget.cpp @@ -31,6 +31,7 @@ along with this program. If not, see . #include #include #include +#include #include #include @@ -694,6 +695,10 @@ QPainterPath GeometryWidget::renderPathToQt(const RenderPath& p) { return path; } +QPainter& GeometryWidget::painter() { + return *m_painter; +} + void GeometryWidget::draw(const RenderPath& p) { setupPainter(); QPainterPath path = renderPathToQt(p); @@ -710,8 +715,46 @@ void GeometryWidget::draw(const RenderPath& p) { void GeometryWidget::drawText(const Point& p, const std::string& text, bool escape) { setupPainter(); QPointF p2 = convertPoint(p); - m_painter->drawText(QRectF(p2 - QPointF{500, 250}, p2 + QPointF{500, 250}), m_textAlignment, + + auto font = m_font; + + if (m_textScalesWithZoom) { + double pixelSize = font.pixelSize() * zoomFactor(); + double pointSize = pixelSize * 72.0 / screen()->logicalDotsPerInch(); + if (pointSize < 0.375) return; + font.setPointSizeF(pointSize); + } + + QFontMetricsF m(font); + + double w = m.horizontalAdvance(QString::fromStdString(text)); + double h = m.height(); + + QRectF rect; + double x = p2.x(); + double y = p2.y(); + + if (m_textAlignment & Qt::AlignHCenter) + rect.setLeft(x - w / 2); + else if (m_textAlignment & Qt::AlignRight) + rect.setLeft(x - w); + else + rect.setLeft(x); + + if (m_textAlignment & Qt::AlignVCenter) + rect.setTop(y - h / 2); + else if (m_textAlignment & Qt::AlignBottom) + rect.setTop(y - h); + else + rect.setTop(y); + + rect.setSize(QSizeF(w, h)); + + m_painter->save(); + m_painter->setFont(font); + m_painter->drawText(rect, m_textAlignment, QString::fromStdString(text)); + m_painter->restore(); } void GeometryWidget::drawImage(const Box& target, const QImage& image, const Box& source, Qt::ImageConversionFlag flags) { @@ -869,6 +912,30 @@ void GeometryWidget::setVerticalTextAlignment(VerticalTextAlignment alignment) { } } +void GeometryWidget::setFontFamily(std::string fontFamily) { + m_font.setFamily(QString::fromStdString(fontFamily)); +} + +void GeometryWidget::setFontSize(double fontSize) { + m_font.setPixelSize(fontSize); +} + +void GeometryWidget::setFontWeight(bool bold) { + m_font.setBold(bold); +} + +void GeometryWidget::useDefaultFont() { + m_font = {}; +} + +void GeometryWidget::setTextFont(QFont font) { + m_font = font; +} + +void GeometryWidget::setTextScaling(bool textScalesWithZoom) { + m_textScalesWithZoom = textScalesWithZoom; +} + void GeometryWidget::addPainting(std::shared_ptr painting, const std::string& name) { bool visible = !m_invisibleLayerNames.contains(name); m_paintings.push_back(DrawnPainting{painting, name, visible}); @@ -972,7 +1039,9 @@ void GeometryWidget::saveToIpe() { IpeRenderer renderer; for (const DrawnPainting& painting : m_paintings) { - renderer.addPainting(painting.m_painting, painting.name); + if (painting.visible) { + renderer.addPainting(painting.m_painting, painting.name); + } } std::filesystem::path filePath = fileName.toStdString(); if (!filePath.has_extension()) { @@ -989,7 +1058,9 @@ void GeometryWidget::saveToSvg() { SvgRenderer renderer; for (const DrawnPainting& painting : m_paintings) { - renderer.addPainting(painting.m_painting, painting.name); + if (painting.visible) { + renderer.addPainting(painting.m_painting, painting.name); + } } auto windowSize = size(); auto bottomLeft = inverseConvertPoint(QPoint(0, windowSize.height())); diff --git a/cartocrow/renderer/geometry_widget.h b/cartocrow/renderer/geometry_widget.h index 5f5b39cb..5a417fde 100644 --- a/cartocrow/renderer/geometry_widget.h +++ b/cartocrow/renderer/geometry_widget.h @@ -218,9 +218,16 @@ class GeometryWidget : public QWidget, public GeometryRenderer { void setLineCap(LineCap lineCap) override; void setHorizontalTextAlignment(HorizontalTextAlignment alignment) override; void setVerticalTextAlignment(VerticalTextAlignment alignment) override; - + void setFontFamily(std::string fontFamily) override; + void setFontSize(double fontSize) override; + void setFontWeight(bool bold) override; + void useDefaultFont() override; + void setTextScaling(bool textScalesWithZoom); + void setTextFont(QFont font); private: QFlags m_textAlignment = Qt::AlignCenter; + bool m_textScalesWithZoom = false; + QFont m_font; public: @@ -305,9 +312,10 @@ class GeometryWidget : public QWidget, public GeometryRenderer { /// Converts a rectangle in Qt coordinates back to drawing coordinates. Box inverseConvertBox(QRectF r) const; /// Convert a render path to a Qt path. - private: QPainterPath renderPathToQt(const RenderPath& p); + QPainter& painter(); + private: /// Converts the polygon to Qt coordinates and adds it to the QPainterPath. void addPolygonToPath(QPainterPath& path, const Polygon& p); diff --git a/cartocrow/renderer/ipe_renderer.cpp b/cartocrow/renderer/ipe_renderer.cpp index 908539df..cc320160 100644 --- a/cartocrow/renderer/ipe_renderer.cpp +++ b/cartocrow/renderer/ipe_renderer.cpp @@ -76,11 +76,6 @@ void IpeRenderer::save(const std::filesystem::path& file) { setFillOpacity(255); // add default alpha to style sheet setStrokeOpacity(255); // add default alpha to style sheet - auto latexPreambleSheet = new ipe::StyleSheet(); - latexPreambleSheet->setPreamble(m_preamble.c_str()); - latexPreambleSheet->setName("latex-preamble"); - document.cascade()->insert(3, latexPreambleSheet); - m_page = new ipe::Page(); document.push_back(m_page); @@ -103,6 +98,18 @@ void IpeRenderer::save(const std::filesystem::path& file) { popStyle(); } + auto latexPreambleSheet = new ipe::StyleSheet(); + latexPreambleSheet->setPreamble(m_preamble.c_str()); + latexPreambleSheet->setName("latex-preamble"); + document.cascade()->insert(3, latexPreambleSheet); + + if (m_usedLocalFont) { + auto fontSpecSheet = new ipe::StyleSheet(); + fontSpecSheet->setPreamble("\\usepackage{fontspec}"); + fontSpecSheet->setName("font-spec"); + document.cascade()->insert(4, fontSpecSheet); + } + auto pdf = file.extension() == ".pdf"; if (pdf) { bool success = document.runLatex(file.c_str()); @@ -290,8 +297,24 @@ void IpeRenderer::draw(const RenderPath& p) { } void IpeRenderer::drawText(const Point& p, const std::string& text, bool escape) { - ipe::String labelText = escape ? escapeForLaTeX(text).data() : text.data(); - ipe::Text* label = new ipe::Text(getAttributesForStyle(), labelText, + std::string labelText = escape ? escapeForLaTeX(text) : text; + std::stringstream fullLabelText; + if (m_style.m_fontFamily.has_value() || m_style.m_fontSize.has_value()) { + m_usedLocalFont = true; + if (m_style.m_fontFamily.has_value()) { + fullLabelText << "\\fontspec{" << (m_style.m_bold ? *m_style.m_fontFamily + " Bold" : *m_style.m_fontFamily) << "}"; + } + if (m_style.m_fontSize.has_value()) { + fullLabelText << "\\fontsize{" << *m_style.m_fontSize << "}{" << (*m_style.m_fontSize + 2) << "}"; + } + fullLabelText << "\\selectfont "; + fullLabelText << labelText; + } else if (m_style.m_bold) { + fullLabelText << "\\textbf{" << labelText << "}"; + } else { + fullLabelText << labelText; + } + ipe::Text* label = new ipe::Text(getAttributesForStyle(), fullLabelText.str().data(), ipe::Vector(p.x(), p.y()), ipe::Text::TextType::ELabel); label->setHorizontalAlignment(m_style.m_horizontalTextAlignment); label->setVerticalAlignment(m_style.m_verticalTextAlignment); @@ -425,6 +448,23 @@ void IpeRenderer::setVerticalTextAlignment(VerticalTextAlignment alignment) { } } +void IpeRenderer::setFontFamily(std::string fontFamily) { + m_style.m_fontFamily = fontFamily; +} + +void IpeRenderer::setFontSize(double fontSize) { + m_style.m_fontSize = fontSize; +} + +void IpeRenderer::useDefaultFont() { + m_style.m_fontSize = std::nullopt; + m_style.m_fontFamily = std::nullopt; +} + +void IpeRenderer::setFontWeight(bool bold) { + m_style.m_bold = bold; +} + void IpeRenderer::drawPathOnPage(ipe::Path* path) { path->setLineCap(m_style.m_lineCap); path->setLineJoin(m_style.m_lineJoin); diff --git a/cartocrow/renderer/ipe_renderer.h b/cartocrow/renderer/ipe_renderer.h index 177e553f..4c7169b4 100644 --- a/cartocrow/renderer/ipe_renderer.h +++ b/cartocrow/renderer/ipe_renderer.h @@ -56,6 +56,12 @@ struct IpeRendererStyle { ipe::THorizontalAlignment m_horizontalTextAlignment = ipe::EAlignHCenter; /// Vertical text alignment ipe::TVerticalAlignment m_verticalTextAlignment = ipe::EAlignVCenter; + /// Bold text? + bool m_bold = false; + /// Local (per text element) font size + std::optional m_fontSize = std::nullopt; + /// Local (per text element) font family + std::optional m_fontFamily = std::nullopt; }; /// Ipe specialization of the GeometryRenderer. @@ -114,6 +120,10 @@ class IpeRenderer : public GeometryRenderer { void setLineCap(LineCap lineCap) override; void setHorizontalTextAlignment(HorizontalTextAlignment alignment) override; void setVerticalTextAlignment(VerticalTextAlignment alignment) override; + void setFontFamily(std::string fontFamily) override; + void setFontSize(double fontSize) override; + void useDefaultFont() override; + void setFontWeight(bool bold) override; void setPreamble(const std::string& preamble); @@ -172,6 +182,8 @@ class IpeRenderer : public GeometryRenderer { int m_pageIndex = 0; /// Latex preamble std::string m_preamble; + /// Whether a font size or family was set; the fontspec package then needs to be added to the preamble. + bool m_usedLocalFont = false; }; } // namespace cartocrow::renderer diff --git a/cartocrow/renderer/painting_renderer.cpp b/cartocrow/renderer/painting_renderer.cpp index 31f62c5d..cce7e27a 100644 --- a/cartocrow/renderer/painting_renderer.cpp +++ b/cartocrow/renderer/painting_renderer.cpp @@ -168,4 +168,21 @@ void PaintingRenderer::setVerticalTextAlignment(VerticalTextAlignment alignment) m_style.m_verticalTextAlignment = alignment; m_objects.push_back(m_style); } + +void PaintingRenderer::setFontFamily(std::string fontFamily) { + m_style.m_fontFamily = fontFamily; +} + +void PaintingRenderer::setFontSize(double fontSize) { + m_style.m_fontSize = fontSize; +} + +void PaintingRenderer::setFontWeight(bool bold) { + m_style.m_bold = bold; +} + +void PaintingRenderer::useDefaultFont() { + m_style.m_fontSize = std::nullopt; + m_style.m_fontFamily = std::nullopt; +} } // namespace cartocrow::renderer diff --git a/cartocrow/renderer/painting_renderer.h b/cartocrow/renderer/painting_renderer.h index 2f392da4..5557583c 100644 --- a/cartocrow/renderer/painting_renderer.h +++ b/cartocrow/renderer/painting_renderer.h @@ -56,6 +56,10 @@ class PaintingRenderer : public GeometryPainting, public GeometryRenderer { void setLineCap(LineCap lineCap) override; void setHorizontalTextAlignment(HorizontalTextAlignment alignment) override; void setVerticalTextAlignment(VerticalTextAlignment alignment) override; + void setFontFamily(std::string fontFamily) override; + void setFontSize(double fontSize) override; + void setFontWeight(bool bold) override; + void useDefaultFont() override; private: struct Style { @@ -88,6 +92,12 @@ class PaintingRenderer : public GeometryPainting, public GeometryRenderer { HorizontalTextAlignment m_horizontalTextAlignment = AlignHCenter; /// Vertical text alignment VerticalTextAlignment m_verticalTextAlignment = AlignVCenter; + /// Bold text? + bool m_bold = false; + /// Local (per text element) font size + std::optional m_fontSize = std::nullopt; + /// Local (per text element) font family + std::optional m_fontFamily = std::nullopt; }; using Label = std::tuple, std::string, bool>; using DrawableObject = diff --git a/cartocrow/renderer/svg_renderer.cpp b/cartocrow/renderer/svg_renderer.cpp index 5114b8f8..e8e9b440 100644 --- a/cartocrow/renderer/svg_renderer.cpp +++ b/cartocrow/renderer/svg_renderer.cpp @@ -201,6 +201,10 @@ void SvgRenderer::draw(const RenderPath& p) { void SvgRenderer::drawText(const Point& p, const std::string& text, bool escape) { m_out << "" << (escape ? escapeForSvg(text) : text) << "\n"; } @@ -405,4 +409,20 @@ void SvgRenderer::setVerticalTextAlignment(VerticalTextAlignment alignment) { } } +void SvgRenderer::setFontFamily(std::string fontFamily) { + m_style.m_fontFamily = fontFamily; +} + +void SvgRenderer::setFontSize(double fontSize) { + m_style.m_fontSize = fontSize; +} + +void SvgRenderer::setFontWeight(bool bold) { + m_style.m_bold = bold; +} + +void SvgRenderer::useDefaultFont() { + m_style.m_fontSize = std::nullopt; + m_style.m_fontFamily = std::nullopt; +} } // namespace cartocrow::renderer diff --git a/cartocrow/renderer/svg_renderer.h b/cartocrow/renderer/svg_renderer.h index 5ae53a94..375b72bb 100644 --- a/cartocrow/renderer/svg_renderer.h +++ b/cartocrow/renderer/svg_renderer.h @@ -57,6 +57,12 @@ struct SvgRendererStyle { std::string m_horizontalTextAlignment = "middle"; /// Vertical text alignment. std::string m_verticalTextAlignment = "middle"; + /// Bold text? + bool m_bold = false; + /// Local (per text element) font size + std::optional m_fontSize = std::nullopt; + /// Local (per text element) font family + std::optional m_fontFamily = std::nullopt; }; /// SVG specialization of the GeometryRenderer. @@ -96,6 +102,10 @@ class SvgRenderer : public GeometryRenderer { void setLineCap(LineCap lineCap) override; void setHorizontalTextAlignment(HorizontalTextAlignment alignment) override; void setVerticalTextAlignment(VerticalTextAlignment alignment) override; + void setFontFamily(std::string fontFamily) override; + void setFontSize(double fontSize) override; + void setFontWeight(bool bold) override; + void useDefaultFont() override; void addPainting(const std::shared_ptr& painting); void addPainting(const std::shared_ptr& painting, const std::string& name);