diff --git a/include/OpenShot.h b/include/OpenShot.h index c5778f99..5273ff0d 100644 --- a/include/OpenShot.h +++ b/include/OpenShot.h @@ -135,7 +135,9 @@ #include "PlayerBase.h" #include "Point.h" #include "Profiles.h" +#include "QtHtmlReader.h" #include "QtImageReader.h" +#include "QtTextReader.h" #include "Timeline.h" #include "Settings.h" diff --git a/include/QtHtmlReader.h b/include/QtHtmlReader.h new file mode 100644 index 00000000..ca5f45c4 --- /dev/null +++ b/include/QtHtmlReader.h @@ -0,0 +1,145 @@ +/** + * @file + * @brief Header file for QtHtmlReader class + * @author Jonathan Thomas + * @author Sergei Kolesov (jediserg) + * @author Jeff Shillitto (jeffski) + * + * @ref License + */ + +/* LICENSE + * + * Copyright (c) 2008-2019 OpenShot Studios, LLC + * . This file is part of + * OpenShot Library (libopenshot), an open-source project dedicated to + * delivering high quality video editing and animation solutions to the + * world. For more information visit . + * + * OpenShot Library (libopenshot) is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * OpenShot Library (libopenshot) 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenShot Library. If not, see . + */ + +#ifndef OPENSHOT_QT_HTML_READER_H +#define OPENSHOT_QT_HTML_READER_H + +#include "ReaderBase.h" + +#include +#include +#include +#include +#include +#include +#include "CacheMemory.h" +#include "Enums.h" +#include "Exceptions.h" + +class QImage; + +namespace openshot +{ + + /** + * @brief This class uses Qt libraries, to create frames with rendered HTML, and return + * openshot::Frame objects. + * + * Supports HTML/CSS subset available via Qt libraries, see: https://doc.qt.io/qt-5/richtext-html-subset.html + * + * @code + * // Any application using this class must instantiate either QGuiApplication or QApplication + * QApplication a(argc, argv); + * + * // Create a reader to generate an openshot::Frame containing text + * QtHtmlReader r(720, // width + * 480, // height + * 5, // x_offset + * 5, // y_offset + * GRAVITY_CENTER, // gravity + * "Check out this Text!", // html + * "b { color: #ff0000 }", // css + * "#000000" // background_color + * ); + * r.Open(); // Open the reader + * + * // Get frame number 1 from the video (in fact, any frame # you request will return the same frame) + * std::shared_ptr f = r.GetFrame(1); + * + * // Now that we have an openshot::Frame object, lets have some fun! + * f->Display(); // Display the frame on the screen + * + * // Close the reader + * r.Close(); + * @endcode + */ + class QtHtmlReader : public ReaderBase + { + private: + int width; + int height; + int x_offset; + int y_offset; + std::string html; + std::string css; + std::string background_color; + std::shared_ptr image; + bool is_open; + openshot::GravityType gravity; + public: + + /// Default constructor (blank text) + QtHtmlReader(); + + /// @brief Constructor for QtHtmlReader with all parameters. + /// @param width The width of the requested openshot::Frame (not the size of the text) + /// @param height The height of the requested openshot::Frame (not the size of the text) + /// @param x_offset The number of pixels to offset the text on the X axis (horizontal) + /// @param y_offset The number of pixels to offset the text on the Y axis (vertical) + /// @param gravity The alignment / gravity of the text + /// @param html The HTML you want to render / display + /// @param css The CSS you want to apply to style the HTML + /// @param background_color The background color of the frame image (valid values are a color string in #RRGGBB or #AARRGGBB notation, a CSS color name, or 'transparent') + QtHtmlReader(int width, int height, int x_offset, int y_offset, GravityType gravity, std::string html, std::string css, std::string background_color); + + /// Close Reader + void Close(); + + /// Get the cache object used by this reader (always returns NULL for this object) + openshot::CacheMemory* GetCache() { return NULL; }; + + /// Get an openshot::Frame object for a specific frame number of this reader. All numbers + /// return the same Frame, since they all share the same image data. + /// + /// @returns The requested frame (containing the image) + /// @param requested_frame The frame number that is requested. + std::shared_ptr GetFrame(int64_t requested_frame); + + /// Determine if reader is open or closed + bool IsOpen() { return is_open; }; + + /// Return the type name of the class + std::string Name() { return "QtHtmlReader"; }; + + /// Get and Set JSON methods + std::string Json(); ///< Generate JSON string of this object + void SetJson(std::string value); ///< Load JSON string into this object + Json::Value JsonValue(); ///< Generate Json::JsonValue for this object + void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + + /// Open Reader - which is called by the constructor automatically + void Open(); + }; + +} + +#endif diff --git a/include/QtTextReader.h b/include/QtTextReader.h new file mode 100644 index 00000000..3bcb2236 --- /dev/null +++ b/include/QtTextReader.h @@ -0,0 +1,159 @@ +/** + * @file + * @brief Header file for QtTextReader class + * @author Jonathan Thomas + * @author Sergei Kolesov (jediserg) + * @author Jeff Shillitto (jeffski) + * + * @ref License + */ + +/* LICENSE + * + * Copyright (c) 2008-2019 OpenShot Studios, LLC + * . This file is part of + * OpenShot Library (libopenshot), an open-source project dedicated to + * delivering high quality video editing and animation solutions to the + * world. For more information visit . + * + * OpenShot Library (libopenshot) is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * OpenShot Library (libopenshot) 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenShot Library. If not, see . + */ + +#ifndef OPENSHOT_QT_TEXT_READER_H +#define OPENSHOT_QT_TEXT_READER_H + +#include "ReaderBase.h" + +#include +#include +#include +#include +#include +#include +#include "CacheMemory.h" +#include "Enums.h" +#include "Exceptions.h" + +class QImage; + +namespace openshot +{ + + /** + * @brief This class uses Qt libraries, to create frames with "Text", and return + * openshot::Frame objects. + * + * All system fonts are supported, including many different font properties, such as size, color, + * alignment, padding, etc... + * + * @code + * // Any application using this class must instantiate either QGuiApplication or QApplication + * QApplication a(argc, argv); + * + * // Create a reader to generate an openshot::Frame containing text + * QtTextReader r(720, // width + * 480, // height + * 5, // x_offset + * 5, // y_offset + * GRAVITY_CENTER, // gravity + * "Check out this Text!", // text + * "Arial", // font + * 15.0, // font size + * "#fff000", // text_color + * "#000000" // background_color + * ); + * r.Open(); // Open the reader + * + * // Get frame number 1 from the video (in fact, any frame # you request will return the same frame) + * std::shared_ptr f = r.GetFrame(1); + * + * // Now that we have an openshot::Frame object, lets have some fun! + * f->Display(); // Display the frame on the screen + * + * // Close the reader + * r.Close(); + * @endcode + */ + class QtTextReader : public ReaderBase + { + private: + int width; + int height; + int x_offset; + int y_offset; + std::string text; + QFont font; + std::string text_color; + std::string background_color; + std::string text_background_color; + std::shared_ptr image; + bool is_open; + openshot::GravityType gravity; + + public: + + /// Default constructor (blank text) + QtTextReader(); + + /// @brief Constructor for QtTextReader with all parameters. + /// @param width The width of the requested openshot::Frame (not the size of the text) + /// @param height The height of the requested openshot::Frame (not the size of the text) + /// @param x_offset The number of pixels to offset the text on the X axis (horizontal) + /// @param y_offset The number of pixels to offset the text on the Y axis (vertical) + /// @param gravity The alignment / gravity of the text + /// @param text The text you want to generate / display + /// @param font The font of the text + /// @param font_size The size of the text + /// @param is_bold Set to true to make text bold + /// @param is_italic Set to true to make text italic + /// @param text_color The color of the text (valid values are a color string in #RRGGBB or #AARRGGBB notation or a CSS color name) + /// @param background_color The background color of the frame image (valid values are a color string in #RRGGBB or #AARRGGBB notation, a CSS color name, or 'transparent') + QtTextReader(int width, int height, int x_offset, int y_offset, GravityType gravity, std::string text, QFont font, std::string text_color, std::string background_color); + + /// Draw a box under rendered text using the specified color. + /// @param color The background color behind the text (valid values are a color string in #RRGGBB or #AARRGGBB notation or a CSS color name) + void SetTextBackgroundColor(std::string color); + + /// Close Reader + void Close(); + + /// Get the cache object used by this reader (always returns NULL for this object) + openshot::CacheMemory* GetCache() { return NULL; }; + + /// Get an openshot::Frame object for a specific frame number of this reader. All numbers + /// return the same Frame, since they all share the same image data. + /// + /// @returns The requested frame (containing the image) + /// @param requested_frame The frame number that is requested. + std::shared_ptr GetFrame(int64_t requested_frame); + + /// Determine if reader is open or closed + bool IsOpen() { return is_open; }; + + /// Return the type name of the class + std::string Name() { return "QtTextReader"; }; + + /// Get and Set JSON methods + std::string Json(); ///< Generate JSON string of this object + void SetJson(string value); ///< Load JSON string into this object + Json::Value JsonValue(); ///< Generate Json::JsonValue for this object + void SetJsonValue(Json::Value root); ///< Load Json::JsonValue into this object + + /// Open Reader - which is called by the constructor automatically + void Open(); + }; + +} + +#endif diff --git a/include/TextReader.h b/include/TextReader.h index 7b276f7f..0995357d 100644 --- a/include/TextReader.h +++ b/include/TextReader.h @@ -121,7 +121,7 @@ namespace openshot TextReader(int width, int height, int x_offset, int y_offset, GravityType gravity, string text, string font, double size, string text_color, string background_color); /// Draw a box under rendered text using the specified color. - /// @param text_background_color The background color behind the text + /// @param color The background color behind the text void SetTextBackgroundColor(string color); /// Close Reader diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 566fa9da..ddb56b52 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -223,6 +223,9 @@ SET ( OPENSHOT_SOURCE_FILES QtPlayer.cpp Settings.cpp Timeline.cpp + QtTextReader.cpp + QtHtmlReader.cpp + # Qt Video Player ${QT_PLAYER_FILES} @@ -335,7 +338,7 @@ target_link_libraries(openshot PUBLIC ${REQUIRED_LIBRARIES}) # Pick up parameters from OpenMP target and propagate target_link_libraries(openshot PUBLIC OpenMP::OpenMP_CXX) -############### CLI EXECUTABLE ################ +############### CLI EXECUTABLES ################ # Create test executable add_executable(openshot-example examples/Example.cpp) @@ -350,6 +353,9 @@ target_compile_definitions(openshot-example PRIVATE # Link test executable to the new library target_link_libraries(openshot-example openshot) +add_executable(openshot-html-test examples/ExampleHtml.cpp) +target_link_libraries(openshot-html-test openshot Qt5::Gui) + ############### PLAYER EXECUTABLE ################ # Create test executable add_executable(openshot-player Qt/demo/main.cpp) diff --git a/src/QtHtmlReader.cpp b/src/QtHtmlReader.cpp new file mode 100644 index 00000000..e3cdc602 --- /dev/null +++ b/src/QtHtmlReader.cpp @@ -0,0 +1,267 @@ +/** + * @file + * @brief Source file for QtHtmlReader class + * @author Jonathan Thomas + * @author Sergei Kolesov (jediserg) + * @author Jeff Shillitto (jeffski) + * + * @ref License + */ + +/* LICENSE + * + * Copyright (c) 2008-2019 OpenShot Studios, LLC + * . This file is part of + * OpenShot Library (libopenshot), an open-source project dedicated to + * delivering high quality video editing and animation solutions to the + * world. For more information visit . + * + * OpenShot Library (libopenshot) is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * OpenShot Library (libopenshot) 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenShot Library. If not, see . + */ + +#include "../include/QtHtmlReader.h" +#include +#include +#include +#include +#include + +using namespace openshot; + +/// Default constructor (blank text) +QtHtmlReader::QtHtmlReader() : width(1024), height(768), x_offset(0), y_offset(0), html(""), css(""), background_color("#000000"), is_open(false), gravity(GRAVITY_CENTER) +{ + // Open and Close the reader, to populate it's attributes (such as height, width, etc...) + Open(); + Close(); +} + +QtHtmlReader::QtHtmlReader(int width, int height, int x_offset, int y_offset, GravityType gravity, std::string html, std::string css, std::string background_color) +: width(width), height(height), x_offset(x_offset), y_offset(y_offset), gravity(gravity), html(html), css(css), background_color(background_color), is_open(false) +{ + // Open and Close the reader, to populate it's attributes (such as height, width, etc...) + Open(); + Close(); +} + +// Open reader +void QtHtmlReader::Open() +{ + // Open reader if not already open + if (!is_open) + { + // create image + image = std::shared_ptr(new QImage(width, height, QImage::Format_RGBA8888)); + image->fill(QColor(background_color.c_str())); + + //start painting + QPainter painter; + if (!painter.begin(image.get())) { + return; + } + + //set background + painter.setBackground(QBrush(background_color.c_str())); + + //draw text + QTextDocument text_document; + + //disable redo/undo stack as not needed + text_document.setUndoRedoEnabled(false); + + //create the HTML/CSS document + text_document.setTextWidth(width); + text_document.setDefaultStyleSheet(css.c_str()); + text_document.setHtml(html.c_str()); + + int td_height = text_document.documentLayout()->documentSize().height(); + + if (gravity == GRAVITY_TOP_LEFT || gravity == GRAVITY_TOP || gravity == GRAVITY_TOP_RIGHT) { + painter.translate(x_offset, y_offset); + } else if (gravity == GRAVITY_LEFT || gravity == GRAVITY_CENTER || gravity == GRAVITY_RIGHT) { + painter.translate(x_offset, (height - td_height) / 2 + y_offset); + } else if (gravity == GRAVITY_BOTTOM_LEFT || gravity == GRAVITY_BOTTOM_RIGHT || gravity == GRAVITY_BOTTOM) { + painter.translate(x_offset, height - td_height + y_offset); + } + + if (gravity == GRAVITY_TOP_LEFT || gravity == GRAVITY_LEFT || gravity == GRAVITY_BOTTOM_LEFT) { + text_document.setDefaultTextOption(QTextOption(Qt::AlignLeft)); + } else if (gravity == GRAVITY_CENTER || gravity == GRAVITY_TOP || gravity == GRAVITY_BOTTOM) { + text_document.setDefaultTextOption(QTextOption(Qt::AlignHCenter)); + } else if (gravity == GRAVITY_TOP_RIGHT || gravity == GRAVITY_RIGHT|| gravity == GRAVITY_BOTTOM_RIGHT) { + text_document.setDefaultTextOption(QTextOption(Qt::AlignRight)); + } + + // Draw image + text_document.drawContents(&painter); + + painter.end(); + + // Update image properties + info.has_audio = false; + info.has_video = true; + info.file_size = 0; + info.vcodec = "QImage"; + info.width = width; + info.height = height; + info.pixel_ratio.num = 1; + info.pixel_ratio.den = 1; + info.duration = 60 * 60 * 1; // 1 hour duration + info.fps.num = 30; + info.fps.den = 1; + info.video_timebase.num = 1; + info.video_timebase.den = 30; + info.video_length = round(info.duration * info.fps.ToDouble()); + + // Calculate the DAR (display aspect ratio) + Fraction size(info.width * info.pixel_ratio.num, info.height * info.pixel_ratio.den); + + // Reduce size fraction + size.Reduce(); + + // Set the ratio based on the reduced fraction + info.display_ratio.num = size.num; + info.display_ratio.den = size.den; + + // Mark as "open" + is_open = true; + } +} + +// Close reader +void QtHtmlReader::Close() +{ + // Close all objects, if reader is 'open' + if (is_open) + { + // Mark as "closed" + is_open = false; + + // Delete the image + image.reset(); + + info.vcodec = ""; + info.acodec = ""; + } +} + +// Get an openshot::Frame object for a specific frame number of this reader. +std::shared_ptr QtHtmlReader::GetFrame(int64_t requested_frame) +{ + if (image) + { + // Create or get frame object + std::shared_ptr image_frame(new Frame(requested_frame, image->size().width(), image->size().height(), background_color, 0, 2)); + + // Add Image data to frame + image_frame->AddImage(image); + + // return frame object + return image_frame; + } else { + // return empty frame + std::shared_ptr image_frame(new Frame(1, 640, 480, background_color, 0, 2)); + + // return frame object + return image_frame; + } + +} + +// Generate JSON string of this object +std::string QtHtmlReader::Json() { + + // Return formatted string + return JsonValue().toStyledString(); +} + +// Generate Json::JsonValue for this object +Json::Value QtHtmlReader::JsonValue() { + + // Create root json object + Json::Value root = ReaderBase::JsonValue(); // get parent properties + root["type"] = "QtHtmlReader"; + root["width"] = width; + root["height"] = height; + root["x_offset"] = x_offset; + root["y_offset"] = y_offset; + root["html"] = html; + root["css"] = css; + root["background_color"] = background_color; + root["gravity"] = gravity; + + // return JsonValue + return root; +} + +// Load JSON string into this object +void QtHtmlReader::SetJson(std::string value) { + + // Parse JSON string into JSON objects + Json::Value root; + Json::CharReaderBuilder rbuilder; + Json::CharReader* reader(rbuilder.newCharReader()); + + std::string errors; + bool success = reader->parse( value.c_str(), + value.c_str() + value.size(), &root, &errors ); + delete reader; + + if (!success) + // Raise exception + throw InvalidJSON("JSON could not be parsed (or is invalid)", ""); + + try + { + // Set all values that match + SetJsonValue(root); + } + catch (exception e) + { + // Error parsing JSON (or missing keys) + throw InvalidJSON("JSON is invalid (missing keys or invalid data types)", ""); + } +} + +// Load Json::JsonValue into this object +void QtHtmlReader::SetJsonValue(Json::Value root) { + + // Set parent data + ReaderBase::SetJsonValue(root); + + // Set data from Json (if key is found) + if (!root["width"].isNull()) + width = root["width"].asInt(); + if (!root["height"].isNull()) + height = root["height"].asInt(); + if (!root["x_offset"].isNull()) + x_offset = root["x_offset"].asInt(); + if (!root["y_offset"].isNull()) + y_offset = root["y_offset"].asInt(); + if (!root["html"].isNull()) + html = root["html"].asString(); + if (!root["css"].isNull()) + css = root["css"].asString(); + if (!root["background_color"].isNull()) + background_color = root["background_color"].asString(); + if (!root["gravity"].isNull()) + gravity = (GravityType) root["gravity"].asInt(); + + // Re-Open path, and re-init everything (if needed) + if (is_open) + { + Close(); + Open(); + } +} diff --git a/src/QtTextReader.cpp b/src/QtTextReader.cpp new file mode 100644 index 00000000..38240c96 --- /dev/null +++ b/src/QtTextReader.cpp @@ -0,0 +1,290 @@ +/** + * @file + * @brief Source file for QtTextReader class + * @author Jonathan Thomas + * @author Sergei Kolesov (jediserg) + * @author Jeff Shillitto (jeffski) + * + * @ref License + */ + +/* LICENSE + * + * Copyright (c) 2008-2019 OpenShot Studios, LLC + * . This file is part of + * OpenShot Library (libopenshot), an open-source project dedicated to + * delivering high quality video editing and animation solutions to the + * world. For more information visit . + * + * OpenShot Library (libopenshot) is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * OpenShot Library (libopenshot) 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenShot Library. If not, see . + */ + +#include "../include/QtTextReader.h" +#include +#include + +using namespace openshot; + +/// Default constructor (blank text) +QtTextReader::QtTextReader() : width(1024), height(768), x_offset(0), y_offset(0), text(""), font(QFont("Arial", 10)), text_color("#ffffff"), background_color("#000000"), is_open(false), gravity(GRAVITY_CENTER) +{ + // Open and Close the reader, to populate it's attributes (such as height, width, etc...) + Open(); + Close(); +} + +QtTextReader::QtTextReader(int width, int height, int x_offset, int y_offset, GravityType gravity, std::string text, QFont font, std::string text_color, std::string background_color) +: width(width), height(height), x_offset(x_offset), y_offset(y_offset), text(text), font(font), text_color(text_color), background_color(background_color), is_open(false), gravity(gravity) +{ + // Open and Close the reader, to populate it's attributes (such as height, width, etc...) + Open(); + Close(); +} + +void QtTextReader::SetTextBackgroundColor(string color) { + text_background_color = color; + + // Open and Close the reader, to populate it's attributes (such as height, width, etc...) plus the text background color + Open(); + Close(); +} + +// Open reader +void QtTextReader::Open() +{ + // Open reader if not already open + if (!is_open) + { + // create image + image = std::shared_ptr(new QImage(width, height, QImage::Format_RGBA8888)); + image->fill(QColor(background_color.c_str())); + + QPainter painter; + if (!painter.begin(image.get())) { + return; + } + + // set background + if (!text_background_color.empty()) { + painter.setBackgroundMode(Qt::OpaqueMode); + painter.setBackground(QBrush(text_background_color.c_str())); + } + + // set font color + painter.setPen(QPen(text_color.c_str())); + + // set font + painter.setFont(font); + + // Set gravity (map between OpenShot and Qt) + int align_flag = 0; + switch (gravity) + { + case GRAVITY_TOP_LEFT: + align_flag = Qt::AlignLeft | Qt::AlignTop; + break; + case GRAVITY_TOP: + align_flag = Qt::AlignHCenter | Qt::AlignTop; + break; + case GRAVITY_TOP_RIGHT: + align_flag = Qt::AlignRight | Qt::AlignTop; + break; + case GRAVITY_LEFT: + align_flag = Qt::AlignVCenter | Qt::AlignLeft; + break; + case GRAVITY_CENTER: + align_flag = Qt::AlignCenter; + break; + case GRAVITY_RIGHT: + align_flag = Qt::AlignVCenter | Qt::AlignRight; + break; + case GRAVITY_BOTTOM_LEFT: + align_flag = Qt::AlignLeft | Qt::AlignBottom; + break; + case GRAVITY_BOTTOM: + align_flag = Qt::AlignHCenter | Qt::AlignBottom; + break; + case GRAVITY_BOTTOM_RIGHT: + align_flag = Qt::AlignRight | Qt::AlignBottom; + break; + } + + // Draw image + painter.drawText(x_offset, y_offset, width, height, align_flag, text.c_str()); + + painter.end(); + + // Update image properties + info.has_audio = false; + info.has_video = true; + info.file_size = 0; + info.vcodec = "QImage"; + info.width = width; + info.height = height; + info.pixel_ratio.num = 1; + info.pixel_ratio.den = 1; + info.duration = 60 * 60 * 1; // 1 hour duration + info.fps.num = 30; + info.fps.den = 1; + info.video_timebase.num = 1; + info.video_timebase.den = 30; + info.video_length = round(info.duration * info.fps.ToDouble()); + + // Calculate the DAR (display aspect ratio) + Fraction font_size(info.width * info.pixel_ratio.num, info.height * info.pixel_ratio.den); + + // Reduce size fraction + font_size.Reduce(); + + // Set the ratio based on the reduced fraction + info.display_ratio.num = font_size.num; + info.display_ratio.den = font_size.den; + + // Mark as "open" + is_open = true; + } +} + +// Close reader +void QtTextReader::Close() +{ + // Close all objects, if reader is 'open' + if (is_open) + { + // Mark as "closed" + is_open = false; + + // Delete the image + image.reset(); + + info.vcodec = ""; + info.acodec = ""; + } +} + +// Get an openshot::Frame object for a specific frame number of this reader. +std::shared_ptr QtTextReader::GetFrame(int64_t requested_frame) +{ + if (image) + { + // Create or get frame object + std::shared_ptr image_frame(new Frame(requested_frame, image->size().width(), image->size().height(), background_color, 0, 2)); + + // Add Image data to frame + image_frame->AddImage(image); + + // return frame object + return image_frame; + } else { + // return empty frame + std::shared_ptr image_frame(new Frame(1, 640, 480, background_color, 0, 2)); + + // return frame object + return image_frame; + } + +} + +// Generate JSON string of this object +std::string QtTextReader::Json() { + + // Return formatted string + return JsonValue().toStyledString(); +} + +// Generate Json::JsonValue for this object +Json::Value QtTextReader::JsonValue() { + + // Create root json object + Json::Value root = ReaderBase::JsonValue(); // get parent properties + root["type"] = "QtTextReader"; + root["width"] = width; + root["height"] = height; + root["x_offset"] = x_offset; + root["y_offset"] = y_offset; + root["text"] = text; + root["font"] = font.toString().toStdString(); + root["text_color"] = text_color; + root["background_color"] = background_color; + root["text_background_color"] = text_background_color; + root["gravity"] = gravity; + + // return JsonValue + return root; +} + +// Load JSON string into this object +void QtTextReader::SetJson(std::string value) { + + // Parse JSON string into JSON objects + Json::Value root; + Json::CharReaderBuilder rbuilder; + Json::CharReader* reader(rbuilder.newCharReader()); + + std::string errors; + bool success = reader->parse( value.c_str(), + value.c_str() + value.size(), &root, &errors ); + delete reader; + + if (!success) + // Raise exception + throw InvalidJSON("JSON could not be parsed (or is invalid)", ""); + + try + { + // Set all values that match + SetJsonValue(root); + } + catch (exception e) + { + // Error parsing JSON (or missing keys) + throw InvalidJSON("JSON is invalid (missing keys or invalid data types)", ""); + } +} + +// Load Json::JsonValue into this object +void QtTextReader::SetJsonValue(Json::Value root) { + + // Set parent data + ReaderBase::SetJsonValue(root); + + // Set data from Json (if key is found) + if (!root["width"].isNull()) + width = root["width"].asInt(); + if (!root["height"].isNull()) + height = root["height"].asInt(); + if (!root["x_offset"].isNull()) + x_offset = root["x_offset"].asInt(); + if (!root["y_offset"].isNull()) + y_offset = root["y_offset"].asInt(); + if (!root["text"].isNull()) + text = root["text"].asString(); + if (!root["font"].isNull()) + font.fromString(QString::fromStdString(root["font"].asString())); + if (!root["text_color"].isNull()) + text_color = root["text_color"].asString(); + if (!root["background_color"].isNull()) + background_color = root["background_color"].asString(); + if (!root["text_background_color"].isNull()) + text_background_color = root["text_background_color"].asString(); + if (!root["gravity"].isNull()) + gravity = (GravityType) root["gravity"].asInt(); + + // Re-Open path, and re-init everything (if needed) + if (is_open) + { + Close(); + Open(); + } +} diff --git a/src/bindings/python/openshot.i b/src/bindings/python/openshot.i index 241fd1c1..01844834 100644 --- a/src/bindings/python/openshot.i +++ b/src/bindings/python/openshot.i @@ -86,8 +86,10 @@ #include "../../../include/PlayerBase.h" #include "../../../include/Point.h" #include "../../../include/Profiles.h" +#include "../../../include/QtHtmlReader.h" #include "../../../include/QtImageReader.h" #include "../../../include/QtPlayer.h" +#include "../../../include/QtTextReader.h" #include "../../../include/KeyFrame.h" #include "../../../include/RendererBase.h" #include "../../../include/Settings.h" @@ -189,8 +191,10 @@ %include "../../../include/PlayerBase.h" %include "../../../include/Point.h" %include "../../../include/Profiles.h" +%include "../../../include/QtHtmlReader.h" %include "../../../include/QtImageReader.h" %include "../../../include/QtPlayer.h" +%include "../../../include/QtTextReader.h" %include "../../../include/KeyFrame.h" %include "../../../include/RendererBase.h" %include "../../../include/Settings.h" diff --git a/src/bindings/ruby/openshot.i b/src/bindings/ruby/openshot.i index 872bbd55..4cfaff39 100644 --- a/src/bindings/ruby/openshot.i +++ b/src/bindings/ruby/openshot.i @@ -98,8 +98,10 @@ namespace std { #include "../../../include/PlayerBase.h" #include "../../../include/Point.h" #include "../../../include/Profiles.h" +#include "../../../include/QtHtmlReader.h" #include "../../../include/QtImageReader.h" #include "../../../include/QtPlayer.h" +#include "../../../include/QtTextReader.h" #include "../../../include/KeyFrame.h" #include "../../../include/RendererBase.h" #include "../../../include/Settings.h" @@ -185,8 +187,10 @@ namespace std { %include "../../../include/PlayerBase.h" %include "../../../include/Point.h" %include "../../../include/Profiles.h" +%include "../../../include/QtHtmlReader.h" %include "../../../include/QtImageReader.h" %include "../../../include/QtPlayer.h" +%include "../../../include/QtTextReader.h" %include "../../../include/KeyFrame.h" %include "../../../include/RendererBase.h" %include "../../../include/Settings.h" diff --git a/src/examples/ExampleHtml.cpp b/src/examples/ExampleHtml.cpp new file mode 100644 index 00000000..f315e252 --- /dev/null +++ b/src/examples/ExampleHtml.cpp @@ -0,0 +1,103 @@ +/** + * @file + * @brief Source file for QtHtmlReader Example (example app for libopenshot) + * @author Jonathan Thomas + * @author FeRD (Frank Dana) + * + * @ref License + */ + +/* LICENSE + * + * Copyright (c) 2008-2019 OpenShot Studios, LLC + * . This file is part of + * OpenShot Library (libopenshot), an open-source project dedicated to + * delivering high quality video editing and animation solutions to the + * world. For more information visit . + * + * OpenShot Library (libopenshot) is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * OpenShot Library (libopenshot) 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenShot Library. If not, see . + */ + +#include +#include +#include +#include "../../include/OpenShot.h" +//#include "../../include/CrashHandler.h" +#include +#include + +using namespace openshot; + +int main(int argc, char* argv[]) { + + QGuiApplication app(argc, argv); + + std::string html_code = R"html(

Check out this HTML!

)html"; + + std::string css_code = R"css( + * {font-family:sans-serif; font-size:18pt; color:#ffffff;} + #red {color: #ff0000;} + )css"; + +// Create a reader to generate an openshot::Frame containing text +QtHtmlReader r(1280, // width + 720, // height + -16, // x_offset + -16, // y_offset + GRAVITY_BOTTOM_RIGHT, // gravity + html_code, // html + css_code, // css + "#000000" // background_color + ); + + r.Open(); // Open the reader + + r.DisplayInfo(); + + /* WRITER ---------------- */ + FFmpegWriter w("cppHtmlExample.mp4"); + + // Set options + //w.SetAudioOptions(true, "libmp3lame", r.info.sample_rate, r.info.channels, r.info.channel_layout, 128000); + w.SetVideoOptions(true, "libx264", Fraction(30000, 1000), 1280, 720, Fraction(1, 1), false, false, 3000000); + + w.info.metadata["title"] = "testtest"; + w.info.metadata["artist"] = "aaa"; + w.info.metadata["album"] = "bbb"; + w.info.metadata["year"] = "2015"; + w.info.metadata["description"] = "ddd"; + w.info.metadata["comment"] = "eee"; + w.info.metadata["comment"] = "comment"; + w.info.metadata["copyright"] = "copyright OpenShot!"; + + // Open writer + w.Open(); + + for (long int frame = 1; frame <= 100; ++frame) + { + std::shared_ptr f = r.GetFrame(frame); // Same frame every time + w.WriteFrame(f); + } + + // Close writer & reader + w.Close(); + r.Close(); + + // Set a timer with 0 timeout to terminate immediately after + // processing events + QTimer::singleShot(0, &app, SLOT(quit())); + + // Run QGuiApplication to completion + return app.exec(); +} diff --git a/src/examples/ExampleHtml.py b/src/examples/ExampleHtml.py new file mode 100755 index 00000000..1938f2fe --- /dev/null +++ b/src/examples/ExampleHtml.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 + +""" + @file + @brief Python source file for QtHtmlReader example + @author Jonathan Thomas + @author FeRD (Frank Dana) + + @ref License +""" + +# LICENSE +# +# Copyright (c) 2008-2019 OpenShot Studios, LLC +# . This file is part of +# OpenShot Library (libopenshot), an open-source project dedicated to +# delivering high quality video editing and animation solutions to the +# world. For more information visit . +# +# OpenShot Library (libopenshot) is free software: you can redistribute it +# and/or modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# OpenShot Library (libopenshot) 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with OpenShot Library. If not, see . + +import sys +from PyQt5.QtCore import QTimer +from PyQt5.QtGui import QGuiApplication +import openshot + +app = QGuiApplication(sys.argv) + +html_code = """

Check out this HTML!

""" + +css_code = """ + * {font-family:sans-serif; font-size:18pt; color:#ffffff;} + #red {color: #ff0000;} +""" + +# Create a QtHtmlReader +r = openshot.QtHtmlReader(1280, # width + 720, # height + -16, # x offset + -16, # y offset + openshot.GRAVITY_BOTTOM_RIGHT, + html_code, + css_code, + "#000000" # background color + ) + +r.Open() # Open the reader + +r.DisplayInfo() # Display metadata + +# Set up Writer +w = openshot.FFmpegWriter("pyHtmlExample.mp4") + +w.SetVideoOptions(True, "libx264", openshot.Fraction(30000, 1000), 1280, 720, + openshot.Fraction(1, 1), False, False, 3000000) + +w.info.metadata["title"] = "testtest" +w.info.metadata["artist"] = "aaa" +w.info.metadata["album"] = "bbb" +w.info.metadata["year"] = "2015" +w.info.metadata["description"] = "ddd" +w.info.metadata["comment"] = "eee" +w.info.metadata["comment"] = "comment" +w.info.metadata["copyright"] = "copyright OpenShot!" + +# Open the Writer +w.Open() + +# Grab 30 frames from Reader and encode to Writer +for frame in range(100): + f = r.GetFrame(frame) + w.WriteFrame(f) + +# Close out Reader & Writer +w.Close() +r.Close() + +# Set a timer to terminate the app as soon as the event queue empties +QTimer.singleShot(0, app.quit) + +# Run QGuiApplication to completion +sys.exit(app.exec())