add is italic check for font file

This commit is contained in:
Filip Sykala 2022-01-06 11:29:16 +01:00
parent 82ee1c5e4a
commit b1b8eee3c9
4 changed files with 82 additions and 81 deletions

View file

@ -45,12 +45,12 @@ std::optional<stbtt_fontinfo> Private::load_font_info(const Emboss::Font &font)
{
int font_offset = stbtt_GetFontOffsetForIndex(font.buffer.data(), font.index);
if (font_offset < 0) {
std::cerr << "Font index("<<font.index<<") doesn't exist.";
std::cerr << "Font index("<<font.index<<") doesn't exist." << std::endl;
return {};
}
stbtt_fontinfo font_info;
if (stbtt_InitFont(&font_info, font.buffer.data(), font_offset) == 0) {
std::cerr << "Can't initialize font.";
std::cerr << "Can't initialize font." << std::endl;
return {};
}
return font_info;
@ -437,7 +437,7 @@ std::optional<Emboss::Font> Emboss::load_font(std::vector<unsigned char> data)
--index; // last one is bad
// at least one font must be inside collection
if (index < 1) {
std::cerr << "There is no font collection inside data.";
std::cerr << "There is no font collection inside data." << std::endl;
return {};
}
// select default font on index 0
@ -457,18 +457,18 @@ std::optional<Emboss::Font> Emboss::load_font(const char *file_path)
{
FILE *file = fopen(file_path, "rb");
if (file == nullptr) {
std::cerr << "Couldn't open " << file_path << " for reading.";
std::cerr << "Couldn't open " << file_path << " for reading." << std::endl;
return {};
}
// find size of file
if (fseek(file, 0L, SEEK_END) != 0) {
std::cerr << "Couldn't fseek file " << file_path << " for size measure.";
std::cerr << "Couldn't fseek file " << file_path << " for size measure." << std::endl;
return {};
}
size_t size = ftell(file);
if (size == 0) {
std::cerr << "Size of font file is zero. Can't read.";
std::cerr << "Size of font file is zero. Can't read." << std::endl;
return {};
}
rewind(file);
@ -476,7 +476,7 @@ std::optional<Emboss::Font> Emboss::load_font(const char *file_path)
std::vector<unsigned char> buffer(size);
size_t count_loaded_bytes = fread((void *) &buffer.front(), 1, size, file);
if (count_loaded_bytes != size) {
std::cerr << "Different loaded(from file) data size.";
std::cerr << "Different loaded(from file) data size." << std::endl;
return {};
}
return load_font(std::move(buffer));
@ -488,7 +488,7 @@ std::optional<Emboss::Font> Emboss::load_font(HFONT hfont)
{
HDC hdc = ::CreateCompatibleDC(NULL);
if (hdc == NULL) {
std::cerr << "Can't create HDC by CreateCompatibleDC(NULL).";
std::cerr << "Can't create HDC by CreateCompatibleDC(NULL)." << std::endl;
return {};
}
@ -506,7 +506,7 @@ std::optional<Emboss::Font> Emboss::load_font(HFONT hfont)
}
if (size == 0 || size == GDI_ERROR) {
std::cerr << "HFONT doesn't have size.";
std::cerr << "HFONT doesn't have size." << std::endl;
::DeleteDC(hdc);
return {};
}
@ -515,7 +515,7 @@ std::optional<Emboss::Font> Emboss::load_font(HFONT hfont)
size_t loaded_size = ::GetFontData(hdc, dwTable, dwOffset, buffer.data(), size);
::DeleteDC(hdc);
if (size != loaded_size) {
std::cerr << "Different loaded(from HFONT) data size.";
std::cerr << "Different loaded(from HFONT) data size." << std::endl;
return {};
}
@ -601,6 +601,43 @@ ExPolygons Emboss::text2shapes(Font & font,
return Private::dilate_to_unique_points(result);
}
bool Emboss::is_italic(Font &font) {
std::optional<stbtt_fontinfo> font_info_opt =
Private::load_font_info(font);
if (!font_info_opt.has_value()) return false;
stbtt_fontinfo *info = &(*font_info_opt);
// https://docs.microsoft.com/cs-cz/typography/opentype/spec/name
// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6name.html
// 2 ==> Style / Subfamily name
int name_id = 2;
int length;
const char* value = stbtt_GetFontNameString(info, &length,
STBTT_PLATFORM_ID_MICROSOFT,
STBTT_MS_EID_UNICODE_BMP,
STBTT_MS_LANG_ENGLISH,
name_id);
// value is big endian utf-16 i need extract only normal chars
std::string value_str;
value_str.reserve(length / 2);
for (int i = 1; i < length; i += 2)
value_str.push_back(value[i]);
// lower case
std::transform(value_str.begin(), value_str.end(), value_str.begin(),
[](unsigned char c) { return std::tolower(c); });
const std::vector<std::string> italics({"italic", "oblique"});
for (const std::string &it : italics) {
if (value_str.find(it) != std::string::npos) {
return true;
}
}
return false;
}
indexed_triangle_set Emboss::polygons2model(const ExPolygons &shape2d,
const IProject &projection)
{

View file

@ -102,6 +102,14 @@ public:
const char * text,
const FontProp &font_prop);
/// <summary>
/// Read information from naming table of font file
/// search for italic (or oblique), bold italic (or bold oblique)
/// </summary>
/// <param name="font">Selector of font</param>
/// <returns>True when the font description contains italic/obligue otherwise False</returns>
static bool is_italic(Font &font);
/// <summary>
/// Project 2d point into space
/// Could be plane, sphere, cylindric, ...

View file

@ -177,4 +177,31 @@ TEST_CASE("triangle intersection", "[]")
Vec2d i = Private::get_intersection(point, dir, triangle);
CHECK(abs(i.x()) < std::numeric_limits<double>::epsilon());
CHECK(abs(i.y() - 1.) < std::numeric_limits<double>::epsilon());
}
#include <string>
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
TEST_CASE("Italic check", "[]")
{
//std::string s1 = "italic";
//std::string s2 = "italic";
//auto pos = s1.find(s2);
//std::cout << ((pos != std::string::npos) ? "good" : "bad");
std::string dir_path = "C:/Windows/Fonts";
for (const auto &entry : fs::directory_iterator(dir_path)) {
if (entry.is_directory()) continue;
const fs::path& act_path = entry.path();
std::string ext = act_path.extension().u8string();
std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c) { return std::tolower(c); });
if (ext != ".ttf") continue;
std::string path_str = act_path.u8string();
auto font_opt = Emboss::load_font(path_str.c_str());
if (!font_opt.has_value()) continue;
std::cout << ((Emboss::is_italic(*font_opt)) ? "[yes] " : "[no ] ")
<< entry.path() << std::endl;
}
}

View file

@ -215,50 +215,6 @@ bool is_similar(const indexed_triangle_set &from,
return true;
}
TEST_CASE("Reduce one edge by Quadric Edge Collapse", "[its]")
{
indexed_triangle_set its;
its.vertices = {Vec3f(-1.f, 0.f, 0.f), Vec3f(0.f, 1.f, 0.f),
Vec3f(1.f, 0.f, 0.f), Vec3f(0.f, 0.f, 1.f),
// vertex to be removed
Vec3f(0.9f, .1f, -.1f)};
its.indices = {Vec3i(1, 0, 3), Vec3i(2, 1, 3), Vec3i(0, 2, 3),
Vec3i(0, 1, 4), Vec3i(1, 2, 4), Vec3i(2, 0, 4)};
// edge to remove is between vertices 2 and 4 on trinagles 4 and 5
indexed_triangle_set its_ = its; // copy
// its_write_obj(its, "tetrhedron_in.obj");
uint32_t wanted_count = its.indices.size() - 1;
its_quadric_edge_collapse(its, wanted_count);
// its_write_obj(its, "tetrhedron_out.obj");
CHECK(its.indices.size() == 4);
CHECK(its.vertices.size() == 4);
for (size_t i = 0; i < 3; i++) {
CHECK(its.indices[i] == its_.indices[i]);
}
for (size_t i = 0; i < 4; i++) {
if (i == 2) continue;
CHECK(its.vertices[i] == its_.vertices[i]);
}
const Vec3f &v = its.vertices[2]; // new vertex
const Vec3f &v2 = its_.vertices[2]; // moved vertex
const Vec3f &v4 = its_.vertices[4]; // removed vertex
for (size_t i = 0; i < 3; i++) {
bool is_between = (v[i] < v4[i] && v[i] > v2[i]) ||
(v[i] > v4[i] && v[i] < v2[i]);
CHECK(is_between);
}
CompareConfig cfg;
cfg.max_average_distance = 0.014f;
cfg.max_distance = 0.75f;
CHECK(is_similar(its, its_, cfg));
CHECK(is_similar(its_, its, cfg));
}
#include "test_utils.hpp"
TEST_CASE("Simplify mesh by Quadric edge collapse to 5%", "[its]")
{
@ -282,30 +238,3 @@ TEST_CASE("Simplify mesh by Quadric edge collapse to 5%", "[its]")
CHECK(is_similar(its, mesh.its, cfg));
}
bool exist_triangle_with_twice_vertices(const std::vector<stl_triangle_vertex_indices>& indices)
{
for (const auto &face : indices)
if (face[0] == face[1] ||
face[0] == face[2] ||
face[1] == face[2]) return true;
return false;
}
TEST_CASE("Simplify trouble case", "[its]")
{
TriangleMesh tm = load_model("simplification.obj");
REQUIRE_FALSE(tm.empty());
float max_error = std::numeric_limits<float>::max();
uint32_t wanted_count = 0;
its_quadric_edge_collapse(tm.its, wanted_count, &max_error);
CHECK(!exist_triangle_with_twice_vertices(tm.its.indices));
}
TEST_CASE("Simplified cube should not be empty.", "[its]")
{
auto its = its_make_cube(1, 2, 3);
float max_error = std::numeric_limits<float>::max();
uint32_t wanted_count = 0;
its_quadric_edge_collapse(its, wanted_count, &max_error);
CHECK(!its.indices.empty());
}