Commit 0bc2c9ea authored by Paul Asmuth's avatar Paul Asmuth
Browse files

implement basic glyph outline loading and embedding

parent 6d815020
Loading
Loading
Loading
Loading
+58 −0
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
 */
#include "font_lookup.h"
#include "sexpr.h"
#include "graphics/geometry.h"
#include "utils/fileutil.h"

#include <iostream>
@@ -21,6 +22,7 @@
#include <fontconfig/fontconfig.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H

using namespace std::placeholders;

@@ -59,6 +61,62 @@ ReturnCode font_load(const std::string& font_file, FontRef* font_ref) {
  return OK;
}

ReturnCode font_get_glyph_path(
    FontRef font,
    double font_size,
    double dpi,
    uint32_t codepoint,
    Path* path) {
  /* load the glyph using freetype */
  auto font_size_ft = font_size * (72.0 / dpi) * 64;
  if (FT_Set_Char_Size(font->ft_font, 0, font_size_ft, dpi, dpi)) {
    return ERROR;
  }

  if (FT_Load_Glyph(font->ft_font, codepoint, FT_LOAD_DEFAULT)) {
    return ERROR;
  }

  FT_Glyph glyph;
  if (FT_Get_Glyph(font->ft_font->glyph, &glyph)) {
    return ERROR;
  }

  if (glyph->format != FT_GLYPH_FORMAT_OUTLINE) {
    return ERROR;
  }

  auto glyph_outline = &((FT_OutlineGlyph) glyph)->outline;

  /* retrieve the glyph outline data from freetype */
  std::vector<Point> glyph_points;
  for (size_t n = 0; n < glyph_outline->n_contours; n++) {
    auto glyph_outline_idx = n == 0 ? 0 : glyph_outline->contours[n - 1] + 1;
    auto glyph_outline_end = glyph_outline->contours[n];

    for (size_t i = glyph_outline_idx; i <= glyph_outline_end; ++i) {
      Point p;
      p.x = glyph_outline->points[i].x / 64;
      p.y = -glyph_outline->points[i].y / 64;

      glyph_points.push_back(p);
    }
  }

  /* convert the glyph outline to a path object */
  *path = Path{};

  for (size_t i = 0; i < glyph_points.size(); ++i) {
    if (i == 0) {
      path->moveTo(glyph_points[i].x, glyph_points[i].y);
    } else {
      path->lineTo(glyph_points[i].x, glyph_points[i].y);
    }
  }

  return OK;
}

bool font_find_fc(
    const std::string& font_pattern,
    std::string* font_file) {
+9 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#pragma once
#include <memory>
#include "return_code.h"
#include "path.h"

namespace fviz {

@@ -41,9 +42,17 @@ enum DefaultFont {

ReturnCode font_load(const std::string& font_file, FontRef* font);

ReturnCode font_get_glyph_path(
    FontRef font,
    double font_size,
    double dpi,
    uint32_t codepoint,
    Path* path);

ReturnCode font_find(DefaultFont font_name, FontInfo* font_info);

ReturnCode font_find_expr(const Expr* expr, FontInfo* font_info);


} // namespace fviz
+73 −33
Original line number Diff line number Diff line
@@ -73,39 +73,6 @@ std::string svg_body(const std::string& in) {
  return out;
}

Status svg_text_span(
    const layer_ops::TextSpanOp& op,
    SVGDataRef svg) {
  const auto& style = op.style;

  std::string transform;
  if (op.rotate) {
    transform = svg_attr(
        "transform",
        fmt::format("rotate({} {} {})",
        op.rotate,
        op.rotate_pivot.x,
        op.rotate_pivot.y));
  }

  svg->buffer
    << "  "
    << "<text"
    << svg_attr("x", op.origin.x)
    << svg_attr("y", op.origin.y)
    << svg_attr("fill", style.color.to_hex_str())
    << svg_attr("font-size", style.font_size)
    << svg_attr("font-family", style.font.font_family_css)
    << svg_attr("font-weight", style.font.font_weight_css)
    << transform
    << ">"
    << svg_body(op.text)
    << "</text>"
    << "\n";

  return OK;
}

std::string svg_path_data(const Path& path) {
  std::stringstream path_data;
  for (const auto& cmd : path) {
@@ -168,6 +135,79 @@ Status svg_fill_path(
  return OK;
}

Status svg_text_span_native(
    const layer_ops::TextSpanOp& op,
    SVGDataRef svg) {
  const auto& style = op.style;

  std::string transform;
  if (op.rotate) {
    transform = svg_attr(
        "transform",
        fmt::format("rotate({} {} {})",
        op.rotate,
        op.rotate_pivot.x,
        op.rotate_pivot.y));
  }

  svg->buffer
    << "  "
    << "<text"
    << svg_attr("x", op.origin.x)
    << svg_attr("y", op.origin.y)
    << svg_attr("fill", style.color.to_hex_str())
    << svg_attr("font-size", style.font_size)
    << svg_attr("font-family", style.font.font_family_css)
    << svg_attr("font-weight", style.font.font_weight_css)
    << transform
    << ">"
    << svg_body(op.text)
    << "</text>"
    << "\n";

  return OK;
}

Status svg_text_span_embed(
    const layer_ops::TextSpanOp& op,
    SVGDataRef svg) {
  const auto& style = op.style;

  for (const auto& g : op.glyphs) {
    Path gp;

    auto rc = font_get_glyph_path(
        op.style.font.font,
        op.style.font_size,
        96, // FIXME
        g.codepoint,
        &gp);

    if (!rc) {
      return ERROR;
    }

    auto gt = fmt::format("translate({} {})", g.x, g.y);

    svg->buffer
        << "  "
        << "<path"
        << svg_attr("fill", style.color.to_hex_str())
        << svg_attr("d", svg_path_data(gp))
        << svg_attr("transform", gt)
        << "/>"
        << "\n";
  }

  return OK;
}

Status svg_text_span(
    const layer_ops::TextSpanOp& op,
    SVGDataRef svg) {
  return svg_text_span_embed(op, svg);
}

std::string SVGData::to_svg() const {
  std::stringstream svg;
  svg