* Fixed some resource leaks in text_producer.
* Load font list each time a text_producer is created to discover fonts uploaded after start.
producer/separated/separated_producer.cpp
producer/text/text_producer.cpp
+ producer/text/utils/freetype_library.cpp
producer/text/utils/texture_atlas.cpp
producer/text/utils/texture_font.cpp
producer/scene/xml_scene_producer.h
producer/text/utils/color.h
+ producer/text/utils/freetype_library.h
producer/text/utils/string_metrics.h
producer/text/utils/text_info.h
producer/text/utils/texture_atlas.h
#include "utils/texture_atlas.h"
#include "utils/texture_font.h"
+#include "utils/freetype_library.h"
class font_comparer {
const std::wstring& lhs;
using namespace boost::filesystem;
-std::map<std::wstring, std::wstring> g_fonts;
-
std::map<std::wstring, std::wstring> enumerate_fonts()
{
std::map<std::wstring, std::wstring> result;
- FT_Library lib;
- FT_Error err = FT_Init_FreeType(&lib);
- if(err)
- return result;
-
- auto fonts = directory_iterator(env::font_folder());
- auto end = directory_iterator();
- for(; fonts != end; ++fonts)
+ for(auto iter = directory_iterator(env::font_folder()), end = directory_iterator(); iter != end; ++iter)
{
- auto file = (*fonts);
+ auto file = (*iter);
if(is_regular_file(file.path()))
{
- FT_Face face;
- err = FT_New_Face(lib, u8(file.path().native()).c_str(), 0, &face);
- if(err)
- continue;
-
- const char* fontname = FT_Get_Postscript_Name(face); //this doesn't work for .fon fonts. Ignoring those for now
+ auto face = get_new_face(u8(file.path().native()));
+ const char* fontname = FT_Get_Postscript_Name(face.get()); //this doesn't work for .fon fonts. Ignoring those for now
if(fontname != nullptr)
{
std::string fontname_str(fontname);
result.insert(std::make_pair(u16(fontname_str), u16(file.path().native())));
}
-
- FT_Done_Face(face);
}
}
- FT_Done_FreeType(lib);
-
return result;
}
+std::vector<std::pair<std::wstring, std::wstring>> list_fonts()
+{
+ auto fonts = enumerate_fonts();
+ return std::vector<std::pair<std::wstring, std::wstring>>(fonts.begin(), fonts.end());
+}
+
void describe_text_producer(help_sink&, const help_repository&);
spl::shared_ptr<frame_producer> create_text_producer(const frame_producer_dependencies&, const std::vector<std::wstring>&);
void init(module_dependencies dependencies)
{
- g_fonts = enumerate_fonts();
dependencies.producer_registry->register_producer_factory(L"Text Producer", create_text_producer, describe_text_producer);
}
text_info& find_font_file(text_info& info)
{
auto& font_name = info.font;
- auto it = std::find_if(g_fonts.begin(), g_fonts.end(), font_comparer(font_name));
- info.font_file = (it != g_fonts.end()) ? (*it).second : L"";
+ auto fonts = enumerate_fonts();
+ auto it = std::find_if(fonts.begin(), fonts.end(), font_comparer(font_name));
+ info.font_file = (it != fonts.end()) ? (*it).second : L"";
return info;
}
struct text_producer::impl
{
- monitor::subject monitor_subject_;
- spl::shared_ptr<core::frame_factory> frame_factory_;
- constraints constraints_;
- int x_, y_, parent_width_, parent_height_;
- bool standalone_;
- variable_impl<std::wstring> text_;
- std::shared_ptr<void> text_subscription_;
- variable_impl<double> tracking_;
- std::shared_ptr<void> tracking_subscription_;
- variable_impl<double> current_bearing_y_;
- variable_impl<double> current_protrude_under_y_;
- draw_frame frame_;
- text::texture_atlas atlas_ { 512, 512, 4 };
- text::texture_font font_;
- bool dirty_ = false;
+ monitor::subject monitor_subject_;
+ spl::shared_ptr<core::frame_factory> frame_factory_;
+ int x_;
+ int y_;
+ int parent_width_;
+ int parent_height_;
+ bool standalone_;
+ constraints constraints_ { parent_width_, parent_height_ };
+ variable_impl<std::wstring> text_;
+ std::shared_ptr<void> text_subscription_;
+ variable_impl<double> tracking_;
+ std::shared_ptr<void> tracking_subscription_;
+ variable_impl<double> current_bearing_y_;
+ variable_impl<double> current_protrude_under_y_;
+ draw_frame frame_;
+ text::texture_atlas atlas_ { 512, 512, 4 };
+ text::texture_font font_;
+ bool dirty_ = false;
public:
explicit impl(const spl::shared_ptr<frame_factory>& frame_factory, int x, int y, const std::wstring& str, text::text_info& text_info, long parent_width, long parent_height, bool standalone)
: frame_factory_(frame_factory)
- , constraints_(parent_width, parent_height)
- , x_(x), y_(y), parent_width_(parent_width), parent_height_(parent_height)
+ , x_(x), y_(y)
+ , parent_width_(parent_width), parent_height_(parent_height)
, standalone_(standalone)
, font_(atlas_, text::find_font_file(text_info), !standalone)
{
sink.para()->text(L"Examples:");
sink.example(L">> PLAY 1-10 [TEXT] \"John Doe\" 0 0 FONT ArialMT SIZE 30 COLOR #1b698d STANDALONE 1");
sink.example(L">> CALL 1-10 \"Jane Doe\"", L"for modifying the text while playing.");
+ sink.para()->text(L"See ")->see(L"FLS")->text(L" for listing the available fonts.");
}
spl::shared_ptr<frame_producer> create_text_producer(const frame_producer_dependencies& dependencies, const std::vector<std::wstring>& params)
#include <boost/property_tree/ptree_fwd.hpp>
#include <string>
#include <vector>
+#include <utility>
#include "utils/color.h"
#include "utils/string_metrics.h"
namespace text
{
void init(module_dependencies dependencies);
+ std::vector<std::pair<std::wstring, std::wstring>> list_fonts();
}
class text_producer : public frame_producer_base
--- /dev/null
+/*
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
+*
+* This file is part of CasparCG (www.casparcg.com).
+*
+* CasparCG is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* CasparCG is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
+*
+* Author: Helge Norberg, helge.norberg@svt.se
+*/
+
+#include "freetype_library.h"
+
+#include <boost/thread/tss.hpp>
+
+namespace caspar { namespace core { namespace text {
+
+spl::shared_ptr<std::remove_pointer<FT_Library>::type> get_lib_for_thread()
+{
+ typedef std::remove_pointer<FT_Library>::type non_ptr_type;
+ static boost::thread_specific_ptr<spl::shared_ptr<non_ptr_type>> libs;
+
+ auto result = libs.get();
+
+ if (!result)
+ {
+ FT_Library raw_lib;
+
+ if (FT_Init_FreeType(&raw_lib))
+ CASPAR_THROW_EXCEPTION(freetype_exception() << msg_info("Failed to initialize freetype"));
+
+ auto lib = spl::shared_ptr<non_ptr_type>(raw_lib, FT_Done_FreeType);
+ result = new spl::shared_ptr<non_ptr_type>(std::move(lib));
+
+ libs.reset(result);
+ }
+
+ return *result;
+}
+
+spl::shared_ptr<std::remove_pointer<FT_Face>::type> get_new_face(const std::string& font_file)
+{
+ auto lib = get_lib_for_thread();
+ FT_Face face;
+ if (FT_New_Face(lib.get(), u8(font_file).c_str(), 0, &face))
+ CASPAR_THROW_EXCEPTION(freetype_exception() << msg_info("Failed to load font file \"" + font_file + "\""));
+
+ return spl::shared_ptr<std::remove_pointer<FT_Face>::type>(face, [lib](auto* p)
+ {
+ FT_Done_Face(p);
+ });
+}
+
+}}}
--- /dev/null
+/*
+* Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
+*
+* This file is part of CasparCG (www.casparcg.com).
+*
+* CasparCG is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* CasparCG is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
+*
+* Author: Helge Norberg, helge.norberg@svt.se
+*/
+
+#pragma once
+
+#include <common/except.h>
+#include <common/memory.h>
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#include <string>
+#include <type_traits>
+
+namespace caspar { namespace core { namespace text {
+
+struct freetype_exception : virtual caspar_exception { };
+
+spl::shared_ptr<std::remove_pointer<FT_Library>::type> get_lib_for_thread();
+spl::shared_ptr<std::remove_pointer<FT_Face>::type> get_new_face(const std::string& font_file);
+
+}}}
#include "texture_atlas.h"
#include "texture_font.h"
+#include "freetype_library.h"
#include <map>
#include <memory>
namespace caspar { namespace core { namespace text {
-struct freetype_exception : virtual caspar_exception { };
-
struct unicode_range
{
unicode_range() : first(0), last(0) {}
int width, height;
};
- std::shared_ptr<FT_LibraryRec_> lib_;
- std::shared_ptr<FT_FaceRec_> face_;
+ spl::shared_ptr<FT_FaceRec_> face_;
texture_atlas atlas_;
double size_;
double tracking_;
std::map<int, glyph_info> glyphs_;
public:
- impl(texture_atlas& atlas, const text_info& info, bool normalize_coordinates) : atlas_(atlas), size_(info.size), tracking_(info.size*info.tracking/1000.0), normalize_(normalize_coordinates)
+ impl(texture_atlas& atlas, const text_info& info, bool normalize_coordinates)
+ : face_(get_new_face(u8(info.font_file)))
+ , atlas_(atlas)
+ , size_(info.size)
+ , tracking_(info.size*info.tracking/1000.0)
+ , normalize_(normalize_coordinates)
{
- FT_Library lib;
-
- if (FT_Init_FreeType(&lib))
- CASPAR_THROW_EXCEPTION(freetype_exception() << msg_info("Failed to initialize freetype"));
-
- lib_.reset(lib, [](FT_Library ptr) { FT_Done_FreeType(ptr); });
-
- FT_Face face;
-
- if (FT_New_Face(lib_.get(), u8(info.font_file).c_str(), 0, &face))
- CASPAR_THROW_EXCEPTION(freetype_exception() << msg_info(L"Failed to load font " + info.font));
-
- face_.reset(face, [](FT_Face ptr) { FT_Done_Face(ptr); });
-
if (FT_Set_Char_Size(face_.get(), static_cast<FT_F26Dot6>(size_*64), 0, 72, 72))
CASPAR_THROW_EXCEPTION(freetype_exception() << msg_info("Failed to set font size"));
}
void load_glyphs(unicode_block block, const color<double>& col)
{
- FT_Error err;
int flags = FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_NORMAL;
unicode_range range = get_range(block);
if(!glyph_index) //ignore codes that doesn't have a glyph for now. Might want to map these to a special glyph later.
continue;
- err = FT_Load_Glyph(face_.get(), glyph_index, flags);
+ FT_Error err = FT_Load_Glyph(face_.get(), glyph_index, flags);
if(err) continue; //igonore glyphs that fail to load
const FT_Bitmap& bitmap = face_->glyph->bitmap; //shorthand notation
#include <core/producer/transition/transition_producer.h>
#include <core/frame/audio_channel_layout.h>
#include <core/frame/frame_transform.h>
+#include <core/producer/text/text_producer.h>
#include <core/producer/stage.h>
#include <core/producer/layer.h>
#include <core/mixer/mixer.h>
return boost::to_upper_copy(replyString.str());
}
+void fls_describer(core::help_sink& sink, const core::help_repository& repo)
+{
+ sink.short_description(L"List all fonts.");
+ sink.syntax(L"FLS");
+ sink.para()
+ ->text(L"Lists all font files in the ")->code(L"fonts")->text(L" folder. Use the command ")
+ ->see(L"INFO PATHS")->text(L" to get the path to the ")->code(L"fonts")->text(L" folder.");
+ sink.para()->text(L"Columns in order from left to right are: Font name and font path.");
+}
+
+std::wstring fls_command(command_context& ctx)
+{
+ std::wstringstream replyString;
+ replyString << L"200 FLS OK\r\n";
+
+ for (auto& font : core::text::list_fonts())
+ replyString << L"\"" << font.first << L"\" \"" << font.second << L"\"\r\n";
+
+ replyString << L"\r\n";
+
+ return replyString.str();
+}
+
void tls_describer(core::help_sink& sink, const core::help_repository& repo)
{
sink.short_description(L"List all templates.");
repo.register_command( L"Query Commands", L"CINF", cinf_describer, cinf_command, 1);
repo.register_command( L"Query Commands", L"CLS", cls_describer, cls_command, 0);
+ repo.register_command( L"Query Commands", L"FLS", fls_describer, fls_command, 0);
repo.register_command( L"Query Commands", L"TLS", tls_describer, tls_command, 0);
repo.register_command( L"Query Commands", L"VERSION", version_describer, version_command, 0);
repo.register_command( L"Query Commands", L"INFO", info_describer, info_command, 0);