Vertical alignemnt center is now in 1/3 of the font ascent

Default vertical alignement is center (for closer result to previous unaligneg version)
This commit is contained in:
Filip Sykala - NTB T15p 2023-08-07 15:59:55 +02:00
parent 6c0d7b79e3
commit 36d5353c67
8 changed files with 129 additions and 117 deletions

View File

@ -19,6 +19,10 @@
#include "libslic3r/Line.hpp" #include "libslic3r/Line.hpp"
#include "libslic3r/BoundingBox.hpp" #include "libslic3r/BoundingBox.hpp"
// Experimentaly suggested ration of font ascent by multiple fonts
// to get approx center of normal text line
const double ASCENT_CENTER = 1/3.; // 0.5 is above small letter
using namespace Slic3r; using namespace Slic3r;
using namespace Emboss; using namespace Emboss;
using fontinfo_opt = std::optional<stbtt_fontinfo>; using fontinfo_opt = std::optional<stbtt_fontinfo>;
@ -1207,17 +1211,21 @@ std::optional<Glyph> Emboss::letter2glyph(const FontFile &font,
return priv::get_glyph(*font_info_opt, letter, flatness); return priv::get_glyph(*font_info_opt, letter, flatness);
} }
int Emboss::get_line_height(const FontFile &font, const FontProp &prop) { const FontFile::Info &Emboss::get_font_info(const FontFile &font, const FontProp &prop)
{
unsigned int font_index = prop.collection_number.value_or(0); unsigned int font_index = prop.collection_number.value_or(0);
assert(priv::is_valid(font, font_index)); assert(priv::is_valid(font, font_index));
const FontFile::Info &info = font.infos[font_index]; return font.infos[font_index];
}
int Emboss::get_line_height(const FontFile &font, const FontProp &prop) {
const FontFile::Info &info = get_font_info(font, prop);
int line_height = info.ascent - info.descent + info.linegap; int line_height = info.ascent - info.descent + info.linegap;
line_height += prop.line_gap.value_or(0); line_height += prop.line_gap.value_or(0);
return static_cast<int>(line_height / SHAPE_SCALE); return static_cast<int>(line_height / SHAPE_SCALE);
} }
namespace { namespace {
ExPolygons letter2shapes( ExPolygons letter2shapes(
wchar_t letter, Point &cursor, FontFileWithCache &font_with_cache, const FontProp &font_prop, fontinfo_opt& font_info_cache) wchar_t letter, Point &cursor, FontFileWithCache &font_with_cache, const FontProp &font_prop, fontinfo_opt& font_info_cache)
{ {
@ -1289,12 +1297,12 @@ namespace {
/// <summary> /// <summary>
/// Align shape against pivot /// Align shape against pivot
/// </summary> /// </summary>
/// <param name="type">Horizontal and vertical alignment</param>
/// <param name="shapes">Shapes to align /// <param name="shapes">Shapes to align
/// Prerequisities: shapes are aligned left top</param> /// Prerequisities: shapes are aligned left top</param>
/// <param name="text">To detect end of lines</param> /// <param name="text">To detect end of lines - to be able horizontal center the line</param>
/// <param name="line_height">Height of line for align[in font points]</param> /// <param name="prop">Containe Horizontal and vertical alignment</param>
void align_shape(FontProp::Align type, std::vector<ExPolygons> &shape, const std::wstring &text, int line_height); /// <param name="font">Needed for scale and font size</param>
void align_shape(std::vector<ExPolygons> &shapes, const std::wstring &text, const FontProp &prop, const FontFile &font);
} }
std::vector<ExPolygons> Emboss::text2vshapes(FontFileWithCache &font_with_cache, const std::wstring& text, const FontProp &font_prop, const std::function<bool()>& was_canceled){ std::vector<ExPolygons> Emboss::text2vshapes(FontFileWithCache &font_with_cache, const std::wstring& text, const FontProp &font_prop, const std::function<bool()>& was_canceled){
@ -1319,7 +1327,7 @@ std::vector<ExPolygons> Emboss::text2vshapes(FontFileWithCache &font_with_cache,
result.emplace_back(letter2shapes(letter, cursor, font_with_cache, font_prop, font_info_cache)); result.emplace_back(letter2shapes(letter, cursor, font_with_cache, font_prop, font_info_cache));
} }
align_shape(font_prop.align, result, text, get_line_height(font, font_prop)); align_shape(result, text, font_prop, font);
return result; return result;
} }
@ -1453,8 +1461,7 @@ std::string Emboss::create_range_text(const std::string &text,
double Emboss::get_shape_scale(const FontProp &fp, const FontFile &ff) double Emboss::get_shape_scale(const FontProp &fp, const FontFile &ff)
{ {
size_t font_index = fp.collection_number.value_or(0); const FontFile::Info &info = get_font_info(ff, fp);
const FontFile::Info &info = ff.infos[font_index];
double scale = fp.size_in_mm / (double) info.unit_per_em; double scale = fp.size_in_mm / (double) info.unit_per_em;
// Shape is scaled for store point coordinate as integer // Shape is scaled for store point coordinate as integer
return scale * SHAPE_SCALE; return scale * SHAPE_SCALE;
@ -1929,20 +1936,22 @@ PolygonPoints Emboss::sample_slice(const TextLine &slice, const BoundingBoxes &b
} }
namespace { namespace {
template<typename T> T get_align_y_offset(FontProp::VerticalAlign align, unsigned count_lines, T line_height) float get_align_y_offset(FontProp::VerticalAlign align, unsigned count_lines, const FontFile &ff, const FontProp &fp)
{ {
if (count_lines == 0) assert(count_lines != 0);
return 0; int line_height = get_line_height(ff, fp);
int ascent = get_font_info(ff, fp).ascent / SHAPE_SCALE;
float line_center = static_cast<float>(std::round(ascent * ASCENT_CENTER));
// direction of Y in 2d is from top to bottom // direction of Y in 2d is from top to bottom
// zero is on base line of first line // zero is on base line of first line
switch (align) { switch (align) {
case FontProp::VerticalAlign::center: return ((count_lines - 1) / 2) * line_height + ((count_lines % 2 == 0) ? (line_height / 2) : 0); case FontProp::VerticalAlign::bottom: return line_height * (count_lines - 1);
case FontProp::VerticalAlign::bottom: return (count_lines - 1) * line_height; case FontProp::VerticalAlign::top: return -ascent;
case FontProp::VerticalAlign::top: // no change case FontProp::VerticalAlign::center:
default: break; default:
return -line_center + line_height * (count_lines - 1) / 2.;
} }
return 0;
} }
int32_t get_align_x_offset(FontProp::HorizontalAlign align, const BoundingBox &shape_bb, const BoundingBox &line_bb) int32_t get_align_x_offset(FontProp::HorizontalAlign align, const BoundingBox &shape_bb, const BoundingBox &line_bb)
@ -1956,11 +1965,10 @@ int32_t get_align_x_offset(FontProp::HorizontalAlign align, const BoundingBox &s
return 0; return 0;
} }
void align_shape(FontProp::Align type, std::vector<ExPolygons> &shapes, const std::wstring &text, int line_height) void align_shape(std::vector<ExPolygons> &shapes, const std::wstring &text, const FontProp &prop, const FontFile &font)
{ {
constexpr FontProp::Align no_change(FontProp::HorizontalAlign::left, FontProp::VerticalAlign::top); // Shapes have to match letters in text
if (type == no_change) assert(shapes.size() == text.length());
return; // no alignment
BoundingBox shape_bb; BoundingBox shape_bb;
for (const ExPolygons& shape: shapes) for (const ExPolygons& shape: shapes)
@ -1973,14 +1981,29 @@ void align_shape(FontProp::Align type, std::vector<ExPolygons> &shapes, const st
return line_bb; return line_bb;
}; };
int line_height = get_line_height(font, prop);
unsigned count_lines = get_count_lines(text);
int center_line = get_font_info(font, prop).ascent * ASCENT_CENTER;
int y_offset = get_align_y_offset(prop.align.second, count_lines, font, prop);
// Speed up for left aligned text
if (prop.align.first == FontProp::HorizontalAlign::left){
// already horizontaly aligned
for (ExPolygons shape : shapes)
for (ExPolygon &s : shape)
s.translate(Point(0, y_offset));
return;
}
// Align x line by line
Point offset( Point offset(
get_align_x_offset(type.first, shape_bb, get_line_bb(0)), get_align_x_offset(prop.align.first, shape_bb, get_line_bb(0)),
get_align_y_offset(type.second, get_count_lines(text), line_height)); y_offset);
assert(shapes.size() == text.length());
for (size_t i = 0; i < shapes.size(); ++i) { for (size_t i = 0; i < shapes.size(); ++i) {
wchar_t letter = text[i]; wchar_t letter = text[i];
if (letter == '\n'){ if (letter == '\n'){
offset.x() = get_align_x_offset(type.first, shape_bb, get_line_bb(i+1)); offset.x() = get_align_x_offset(prop.align.first, shape_bb, get_line_bb(i + 1));
continue; continue;
} }
ExPolygons &shape = shapes[i]; ExPolygons &shape = shapes[i];
@ -1990,8 +2013,10 @@ void align_shape(FontProp::Align type, std::vector<ExPolygons> &shapes, const st
} }
} // namespace } // namespace
double Emboss::get_align_y_offset(FontProp::VerticalAlign align, unsigned count_lines, double line_height){ double Emboss::get_align_y_offset_in_mm(FontProp::VerticalAlign align, unsigned count_lines, const FontFile &ff, const FontProp &fp){
return ::get_align_y_offset<double>(align, count_lines, line_height); float offset_in_font_point = get_align_y_offset(align, count_lines, ff, fp);
double scale = get_shape_scale(fp, ff);
return scale * offset_in_font_point;
} }
#ifdef REMOVE_SPIKES #ifdef REMOVE_SPIKES

View File

@ -228,6 +228,14 @@ namespace Emboss
/// <returns>Conversion to mm</returns> /// <returns>Conversion to mm</returns>
double get_shape_scale(const FontProp &fp, const FontFile &ff); double get_shape_scale(const FontProp &fp, const FontFile &ff);
/// <summary>
/// getter of font info by collection defined in prop
/// </summary>
/// <param name="font">Contain infos about all fonts(collections) in file</param>
/// <param name="prop">Index of collection</param>
/// <returns>Ascent, descent, line gap</returns>
const FontFile::Info &get_font_info(const FontFile &font, const FontProp &prop);
/// <summary> /// <summary>
/// Read from font file and properties height of line with spacing /// Read from font file and properties height of line with spacing
/// </summary> /// </summary>
@ -239,12 +247,10 @@ namespace Emboss
/// <summary> /// <summary>
/// Calculate Vertical align /// Calculate Vertical align
/// </summary> /// </summary>
/// <typeparam name="T">double for mm</typeparam> /// <param name="align">Top | Center | Bottom</param>
/// <param name="align">type</param>
/// <param name="count_lines"></param> /// <param name="count_lines"></param>
/// <param name="line_height"></param> /// <returns>Return align Y offset in mm</returns>
/// <returns>In same unit as line height</returns> double get_align_y_offset_in_mm(FontProp::VerticalAlign align, unsigned count_lines, const FontFile &ff, const FontProp &fp);
double get_align_y_offset(FontProp::VerticalAlign align, unsigned count_lines, double line_height);
/// <summary> /// <summary>
/// Project spatial point /// Project spatial point

View File

@ -69,7 +69,7 @@ struct FontProp
using Align = std::pair<HorizontalAlign, VerticalAlign>; using Align = std::pair<HorizontalAlign, VerticalAlign>;
// change pivot of text // change pivot of text
// When not set, center is used and is not stored // When not set, center is used and is not stored
Align align = Align(HorizontalAlign::center, VerticalAlign::top); Align align = Align(HorizontalAlign::center, VerticalAlign::center);
////// //////
// Duplicit data to wxFontDescriptor // Duplicit data to wxFontDescriptor

View File

@ -1094,40 +1094,6 @@ EmbossStyles GLGizmoEmboss::create_default_styles()
} }
namespace{ namespace{
bool get_line_height_offset(/* const*/ StyleManager &style_manager, double &line_height_mm, double &line_offset_mm)
{
assert(style_manager.is_active_font());
if (!style_manager.is_active_font())
return false;
const auto &ffc = style_manager.get_font_file_with_cache();
assert(ffc.has_value());
if (!ffc.has_value())
return false;
const auto &ff_ptr = ffc.font_file;
assert(ff_ptr != nullptr);
if (ff_ptr == nullptr)
return false;
const FontProp &fp = style_manager.get_font_prop();
const FontFile &ff = *ff_ptr;
double third_ascent_shape_size = ff.infos[fp.collection_number.value_or(0)].ascent / 3.;
int line_height_shape_size = get_line_height(ff, fp); // In shape size
double scale = get_shape_scale(fp, ff);
line_offset_mm = third_ascent_shape_size * scale / SHAPE_SCALE;
line_height_mm = line_height_shape_size * scale;
if (line_height_mm < 0)
return false;
// fix for bad filled ascent in font file
if (line_offset_mm <= 0)
line_offset_mm = line_height_mm / 3;
return true;
}
void init_text_lines(TextLinesModel &text_lines, const Selection& selection, /* const*/ StyleManager &style_manager, unsigned count_lines) void init_text_lines(TextLinesModel &text_lines, const Selection& selection, /* const*/ StyleManager &style_manager, unsigned count_lines)
{ {
const GLVolume *gl_volume_ptr = selection.get_first_volume(); const GLVolume *gl_volume_ptr = selection.get_first_volume();
@ -1178,12 +1144,7 @@ void init_text_lines(TextLinesModel &text_lines, const Selection& selection, /*
Transform3d mv_trafo = gl_volume.get_volume_transformation().get_matrix(); Transform3d mv_trafo = gl_volume.get_volume_transformation().get_matrix();
if (tc.fix_3mf_tr.has_value()) if (tc.fix_3mf_tr.has_value())
mv_trafo = mv_trafo * (tc.fix_3mf_tr->inverse()); mv_trafo = mv_trafo * (tc.fix_3mf_tr->inverse());
FontProp::VerticalAlign align = style_manager.get_font_prop().align.second; text_lines.init(mv_trafo, volumes, style_manager, count_lines);
double line_height_mm, line_offset_mm;
if (!get_line_height_offset(style_manager, line_height_mm, line_offset_mm))
return;
text_lines.init(mv_trafo, volumes, align, line_height_mm, line_offset_mm, count_lines);
} }
void init_new_text_line(TextLinesModel &text_lines, const Transform3d& new_text_tr, const ModelObject& mo, /* const*/ StyleManager &style_manager) void init_new_text_line(TextLinesModel &text_lines, const Transform3d& new_text_tr, const ModelObject& mo, /* const*/ StyleManager &style_manager)
@ -1197,13 +1158,8 @@ void init_new_text_line(TextLinesModel &text_lines, const Transform3d& new_text_
continue; continue;
volumes.push_back(volume); volumes.push_back(volume);
} }
FontProp::VerticalAlign align = style_manager.get_font_prop().align.second;
double line_height_mm, line_offset_mm;
if (!get_line_height_offset(style_manager, line_height_mm, line_offset_mm))
return;
unsigned count_lines = 1; unsigned count_lines = 1;
text_lines.init(new_text_tr, volumes, align, line_height_mm, line_offset_mm, count_lines); text_lines.init(new_text_tr, volumes, style_manager, count_lines);
} }
} }
@ -3129,10 +3085,7 @@ void GLGizmoEmboss::draw_advanced()
} }
FontProp &font_prop = m_style_manager.get_font_prop(); FontProp &font_prop = m_style_manager.get_font_prop();
const auto &cn = m_style_manager.get_font_prop().collection_number; const FontFile::Info &font_info = get_font_info(*ff.font_file, font_prop);
unsigned int font_index = (cn.has_value()) ? *cn : 0;
const auto &font_info = ff.font_file->infos[font_index];
#ifdef SHOW_FONT_FILE_PROPERTY #ifdef SHOW_FONT_FILE_PROPERTY
ImGui::SameLine(); ImGui::SameLine();
int cache_size = ff.has_value()? (int)ff.cache->size() : 0; int cache_size = ff.has_value()? (int)ff.cache->size() : 0;

View File

@ -47,10 +47,8 @@ void CreateFontStyleImagesJob::process(Ctl &ctl)
for (ExPolygon &shape : shapes) shape.translate(-bounding_box.min); for (ExPolygon &shape : shapes) shape.translate(-bounding_box.min);
// calculate conversion from FontPoint to screen pixels by size of font // calculate conversion from FontPoint to screen pixels by size of font
const auto &cn = item.prop.collection_number; const FontFile::Info &info = get_font_info(*item.font.font_file, item.prop);
unsigned int font_index = (cn.has_value()) ? *cn : 0; double scale = item.prop.size_in_mm / info.unit_per_em * SHAPE_SCALE * m_input.ppm;
double unit_per_em = item.font.font_file->infos[font_index].unit_per_em;
double scale = item.prop.size_in_mm / unit_per_em * SHAPE_SCALE * m_input.ppm;
scales[index] = scale; scales[index] = scale;
//double scale = font_prop.size_in_mm * SCALING_FACTOR; //double scale = font_prop.size_in_mm * SCALING_FACTOR;

View File

@ -249,22 +249,60 @@ GLModel::Geometry create_geometry(const TextLines &lines)
geometry.add_triangle(t[0], t[1], t[2]); geometry.add_triangle(t[0], t[1], t[2]);
return geometry; return geometry;
} }
bool get_line_height_offset(const FontProp &fp, const FontFile &ff, double &line_height_mm, double &line_offset_mm)
{
double third_ascent_shape_size = get_font_info(ff, fp).ascent / 3.;
int line_height_shape_size = get_line_height(ff, fp); // In shape size
double scale = get_shape_scale(fp, ff);
line_offset_mm = third_ascent_shape_size * scale / SHAPE_SCALE;
line_height_mm = line_height_shape_size * scale;
if (line_height_mm < 0)
return false;
// fix for bad filled ascent in font file
if (line_offset_mm <= 0)
line_offset_mm = line_height_mm / 3;
return true;
}
} // namespace } // namespace
void TextLinesModel::init(const Transform3d &text_tr, void TextLinesModel::init(const Transform3d &text_tr,
const ModelVolumePtrs &volumes_to_slice, const ModelVolumePtrs &volumes_to_slice,
FontProp::VerticalAlign align, /*const*/ Emboss::StyleManager &style_manager,
double line_height,
double offset,
unsigned count_lines) unsigned count_lines)
{ {
assert(style_manager.is_active_font());
if (!style_manager.is_active_font())
return;
const auto &ffc = style_manager.get_font_file_with_cache();
assert(ffc.has_value());
if (!ffc.has_value())
return;
const auto &ff_ptr = ffc.font_file;
assert(ff_ptr != nullptr);
if (ff_ptr == nullptr)
return;
const FontFile &ff = *ff_ptr;
const FontProp &fp = style_manager.get_font_prop();
FontProp::VerticalAlign align = fp.align.second;
double line_height_mm, line_offset_mm;
if (!get_line_height_offset(fp, ff, line_height_mm, line_offset_mm))
return;
m_model.reset(); m_model.reset();
m_lines.clear(); m_lines.clear();
double first_line_center = offset + this->offset + get_align_y_offset(align, count_lines, line_height); double first_line_center = offset + this->offset + get_align_y_offset_in_mm(align, count_lines, ff, fp);
std::vector<float> line_centers(count_lines); std::vector<float> line_centers(count_lines);
for (size_t i = 0; i < count_lines; ++i) for (size_t i = 0; i < count_lines; ++i)
line_centers[i] = static_cast<float>(first_line_center - i * line_height); line_centers[i] = static_cast<float>(first_line_center - i * line_height_mm);
// contour transformation // contour transformation
Transform3d c_trafo = text_tr * get_rotation(); Transform3d c_trafo = text_tr * get_rotation();
@ -357,7 +395,7 @@ void TextLinesModel::render(const Transform3d &text_world)
double TextLinesModel::calc_line_height(const Slic3r::Emboss::FontFile &ff, const FontProp &fp) double TextLinesModel::calc_line_height(const Slic3r::Emboss::FontFile &ff, const FontProp &fp)
{ {
int line_height = Emboss::get_line_height(ff, fp); // In shape size int line_height = Slic3r::Emboss::get_line_height(ff, fp); // In shape size
double scale = Emboss::get_shape_scale(fp, ff); double scale = Slic3r::Emboss::get_shape_scale(fp, ff);
return line_height * scale; return line_height * scale;
} }

View File

@ -6,6 +6,7 @@
#include <libslic3r/Point.hpp> #include <libslic3r/Point.hpp>
#include <libslic3r/Emboss.hpp> #include <libslic3r/Emboss.hpp>
#include "slic3r/GUI/GLModel.hpp" #include "slic3r/GUI/GLModel.hpp"
#include "slic3r/Utils/EmbossStyleManager.hpp"
namespace Slic3r { namespace Slic3r {
class ModelVolume; class ModelVolume;
@ -24,11 +25,9 @@ public:
/// </summary> /// </summary>
/// <param name="text_tr">Transformation of text volume inside object (aka inside of instance)</param> /// <param name="text_tr">Transformation of text volume inside object (aka inside of instance)</param>
/// <param name="volumes_to_slice">Vector of volumes to be sliced</param> /// <param name="volumes_to_slice">Vector of volumes to be sliced</param>
/// <param name="align">Vertical (Y) align of the text</param> /// <param name="style_manager">Contain Font file, size and align</param>
/// <param name="line_height">Distance between lines [in mm]</param> /// <param name="count_lines">Count lines of embossed text(for veritcal alignment)</param>
/// <param name="line_height">Offset from baseline [in mm]</param> void init(const Transform3d &text_tr, const ModelVolumePtrs &volumes_to_slice, /*const*/ Emboss::StyleManager &style_manager, unsigned count_lines);
/// <param name="count_lines">Count lines(slices over volumes)</param>
void init(const Transform3d &text_tr, const ModelVolumePtrs& volumes_to_slice, FontProp::VerticalAlign align, double line_height, double offset, unsigned count_lines);
void render(const Transform3d &text_world); void render(const Transform3d &text_world);

View File

@ -449,12 +449,10 @@ float StyleManager::min_imgui_font_size = 18.f;
float StyleManager::max_imgui_font_size = 60.f; float StyleManager::max_imgui_font_size = 60.f;
float StyleManager::get_imgui_font_size(const FontProp &prop, const FontFile &file, double scale) float StyleManager::get_imgui_font_size(const FontProp &prop, const FontFile &file, double scale)
{ {
const auto &cn = prop.collection_number; const FontFile::Info& info = get_font_info(file, prop);
unsigned int font_index = (cn.has_value()) ? *cn : 0;
const auto &font_info = file.infos[font_index];
// coeficient for convert line height to font size // coeficient for convert line height to font size
float c1 = (font_info.ascent - font_info.descent + font_info.linegap) / float c1 = (info.ascent - info.descent + info.linegap) /
(float) font_info.unit_per_em; (float) info.unit_per_em;
// The point size is defined as 1/72 of the Anglo-Saxon inch (25.4 mm): // The point size is defined as 1/72 of the Anglo-Saxon inch (25.4 mm):
// It is approximately 0.0139 inch or 352.8 um. // It is approximately 0.0139 inch or 352.8 um.
@ -490,17 +488,12 @@ ImFont *StyleManager::create_imgui_font(const std::string &text, double scale)
ImFontConfig font_config; ImFontConfig font_config;
// TODO: start using merge mode // TODO: start using merge mode
//font_config.MergeMode = true; //font_config.MergeMode = true;
int unit_per_em = get_font_info(font_file, font_prop).unit_per_em;
unsigned int font_index = font_prop.collection_number.value_or(0); float coef = font_size / (double) unit_per_em;
const auto &font_info = font_file.infos[font_index]; if (font_prop.char_gap.has_value())
if (font_prop.char_gap.has_value()) {
float coef = font_size / (double) font_info.unit_per_em;
font_config.GlyphExtraSpacing.x = coef * (*font_prop.char_gap); font_config.GlyphExtraSpacing.x = coef * (*font_prop.char_gap);
} if (font_prop.line_gap.has_value())
if (font_prop.line_gap.has_value()) {
float coef = font_size / (double) font_info.unit_per_em;
font_config.GlyphExtraSpacing.y = coef * (*font_prop.line_gap); font_config.GlyphExtraSpacing.y = coef * (*font_prop.line_gap);
}
font_config.FontDataOwnedByAtlas = false; font_config.FontDataOwnedByAtlas = false;