From 9c116b1efd19c1ccefa802dcbe448c262e531b94 Mon Sep 17 00:00:00 2001 From: jmsgrogan Date: Sat, 1 Jan 2022 18:46:31 +0000 Subject: [PATCH] Add PDF writer. --- apps/CMakeLists.txt | 11 +- apps/xml-practice.cpp | 38 ------- src/CMakeLists.txt | 3 + src/core/CMakeLists.txt | 2 + src/core/Dictionary.cpp | 56 ++++++++++ src/core/Dictionary.h | 35 +++++++ src/core/StringUtils.cpp | 9 ++ src/core/StringUtils.h | 1 + src/database/DatabaseManager.cpp | 5 + src/database/DatabaseManager.h | 10 +- src/image/CMakeLists.txt | 21 ++++ src/image/Image.cpp | 69 ++++++++++++ src/image/Image.h | 35 +++++++ src/image/PngWriter.cpp | 74 +++++++++++++ src/image/PngWriter.h | 22 ++++ src/network/NetworkManager.h | 7 +- src/network/sockets/Socket.h | 2 +- src/publishing/CMakeLists.txt | 36 +++++++ src/publishing/pdf/PdfDictionary.cpp | 45 ++++++++ src/publishing/pdf/PdfDictionary.h | 10 ++ src/publishing/pdf/PdfDocument.cpp | 73 +++++++++++++ src/publishing/pdf/PdfDocument.h | 44 ++++++++ src/publishing/pdf/PdfDocumentCatalog.cpp | 44 ++++++++ src/publishing/pdf/PdfDocumentCatalog.h | 23 ++++ src/publishing/pdf/PdfObject.cpp | 58 +++++++++++ src/publishing/pdf/PdfObject.h | 40 +++++++ src/publishing/pdf/PdfOutline.cpp | 40 +++++++ src/publishing/pdf/PdfOutline.h | 26 +++++ src/publishing/pdf/PdfPage.cpp | 0 src/publishing/pdf/PdfPage.h | 121 ++++++++++++++++++++++ src/publishing/pdf/PdfPageTree.cpp | 37 +++++++ src/publishing/pdf/PdfPageTree.h | 23 ++++ src/publishing/pdf/PdfStream.cpp | 36 +++++++ src/publishing/pdf/PdfStream.h | 19 ++++ src/publishing/pdf/PdfWriter.cpp | 12 +++ src/publishing/pdf/PdfWriter.h | 13 +++ src/publishing/pdf/PdfXRefTable.cpp | 68 ++++++++++++ src/publishing/pdf/PdfXRefTable.h | 37 +++++++ src/video/CMakeLists.txt | 30 ++++++ src/video/FfmegInterface.cpp | 118 +++++++++++++++++++++ src/video/FfmpegInterface.h | 15 +++ src/video/Video.cpp | 16 +++ src/video/Video.h | 18 ++++ src/web/CMakeLists.txt | 4 + src/web/html/HtmlDocument.cpp | 16 ++- src/web/html/HtmlDocument.h | 5 +- src/web/html/HtmlDomManager.h | 15 +++ src/web/html/HtmlElement.cpp | 2 +- src/web/html/HtmlElement.h | 7 +- src/web/html/HtmlWriter.cpp | 53 +++++++++- src/web/html/HtmlWriter.h | 7 +- src/web/html/elements/HtmlBodyElement.h | 12 +++ src/web/html/elements/HtmlHeadElement.h | 12 +++ src/web/markdown/MarkdownParser.cpp | 19 +++- src/web/markdown/MarkdownParser.h | 22 +++- src/web/xml/XmlParser.cpp | 40 ++++--- src/web/xml/XmlParser.h | 4 +- src/web/xml/XmlWriter.cpp | 66 +++++++++++- src/web/xml/XmlWriter.h | 7 +- src/web/xml/xml-elements/XmlElement.cpp | 10 ++ src/web/xml/xml-elements/XmlElement.h | 20 ++-- src/web/xml/xml-elements/XmlProlog.h | 3 +- test/CMakeLists.txt | 27 ++++- test/audio/TestAudioWriter.cpp | 10 +- test/data/sample_markdown.md | 20 ++++ test/database/TestDatabase.cpp | 12 +++ test/image/TestPngWriter.cpp | 31 ++++++ test/ipc/TestDbus.cpp | 8 ++ test/publishing/TestPdfWriter.cpp | 18 ++++ test/video/TestVideoDecoder.cpp | 25 +++++ test/web/TestMarkdownParser.cpp | 25 +++++ test/web/TestXmlParser.cpp | 31 ++++++ 72 files changed, 1819 insertions(+), 114 deletions(-) delete mode 100644 apps/xml-practice.cpp create mode 100644 src/core/Dictionary.cpp create mode 100644 src/core/Dictionary.h create mode 100644 src/image/CMakeLists.txt create mode 100644 src/image/Image.cpp create mode 100644 src/image/Image.h create mode 100644 src/image/PngWriter.cpp create mode 100644 src/image/PngWriter.h create mode 100644 src/publishing/CMakeLists.txt create mode 100644 src/publishing/pdf/PdfDictionary.cpp create mode 100644 src/publishing/pdf/PdfDictionary.h create mode 100644 src/publishing/pdf/PdfDocument.cpp create mode 100644 src/publishing/pdf/PdfDocument.h create mode 100644 src/publishing/pdf/PdfDocumentCatalog.cpp create mode 100644 src/publishing/pdf/PdfDocumentCatalog.h create mode 100644 src/publishing/pdf/PdfObject.cpp create mode 100644 src/publishing/pdf/PdfObject.h create mode 100644 src/publishing/pdf/PdfOutline.cpp create mode 100644 src/publishing/pdf/PdfOutline.h create mode 100644 src/publishing/pdf/PdfPage.cpp create mode 100644 src/publishing/pdf/PdfPage.h create mode 100644 src/publishing/pdf/PdfPageTree.cpp create mode 100644 src/publishing/pdf/PdfPageTree.h create mode 100644 src/publishing/pdf/PdfStream.cpp create mode 100644 src/publishing/pdf/PdfStream.h create mode 100644 src/publishing/pdf/PdfWriter.cpp create mode 100644 src/publishing/pdf/PdfWriter.h create mode 100644 src/publishing/pdf/PdfXRefTable.cpp create mode 100644 src/publishing/pdf/PdfXRefTable.h create mode 100644 src/video/CMakeLists.txt create mode 100644 src/video/FfmegInterface.cpp create mode 100644 src/video/FfmpegInterface.h create mode 100644 src/video/Video.cpp create mode 100644 src/video/Video.h create mode 100644 test/data/sample_markdown.md create mode 100644 test/database/TestDatabase.cpp create mode 100644 test/image/TestPngWriter.cpp create mode 100644 test/ipc/TestDbus.cpp create mode 100644 test/publishing/TestPdfWriter.cpp create mode 100644 test/video/TestVideoDecoder.cpp create mode 100644 test/web/TestMarkdownParser.cpp create mode 100644 test/web/TestXmlParser.cpp diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 8c335f3..2b2fc4a 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -25,15 +25,6 @@ target_include_directories(sample_console PUBLIC ) target_link_libraries(sample_console PUBLIC console core network database geometry audio web) - -# Xml practice -add_executable(xml_practice xml-practice.cpp) -target_include_directories(xml_practice PUBLIC - "${PROJECT_SOURCE_DIR}/src/core" - "${PROJECT_SOURCE_DIR}/src/web/xml" - ) -target_link_libraries(xml_practice PUBLIC core web) set_property(TARGET sample_console PROPERTY FOLDER apps) -set_property(TARGET sample_gui PROPERTY FOLDER apps) -set_property(TARGET xml_practice PROPERTY FOLDER apps) \ No newline at end of file +set_property(TARGET sample_gui PROPERTY FOLDER apps) \ No newline at end of file diff --git a/apps/xml-practice.cpp b/apps/xml-practice.cpp deleted file mode 100644 index 913510d..0000000 --- a/apps/xml-practice.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include -#include -#include -#include "CommandLineArgs.h" -#include "XmlParser.h" - -int main(int argc, char *argv[]) -{ - CommandLineArgs command_line_args; - command_line_args.Process(argc, argv); - - if(command_line_args.GetNumberOfArgs() < 2) - { - std::cerr << "Expected a filepath argument" << std::endl; - return -1; - } - - XmlParser parser; - const auto filepath = command_line_args.GetArg(1); - - if(!std::filesystem::exists(filepath)) - { - std::cerr << "Couldn't find file: " << filepath << std::endl; - return -1; - } - - std::ifstream xml_file; - xml_file.open(filepath, std::ifstream::in); - while(xml_file.good()) - { - std::string line; - std::getline(xml_file, line); - parser.ProcessLine(line); - } - xml_file.close(); - - return 0; -} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 294fd2f..f6e0469 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,9 +3,12 @@ add_subdirectory(database) add_subdirectory(network) add_subdirectory(geometry) add_subdirectory(audio) +add_subdirectory(image) add_subdirectory(console) add_subdirectory(client) add_subdirectory(graphics) +add_subdirectory(publishing) +add_subdirectory(video) add_subdirectory(windows) add_subdirectory(web) add_subdirectory(ui_elements) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c19faf8..c6d3a5e 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,5 +1,6 @@ list(APPEND core_HEADERS AbstractApp.h + Dictionary.h Event.h Color.h CommandLineArgs.h @@ -12,6 +13,7 @@ list(APPEND core_HEADERS list(APPEND core_LIB_INCLUDES Event.cpp + Dictionary.cpp Color.cpp CommandLineArgs.cpp loggers/FileLogger.cpp diff --git a/src/core/Dictionary.cpp b/src/core/Dictionary.cpp new file mode 100644 index 0000000..720a75a --- /dev/null +++ b/src/core/Dictionary.cpp @@ -0,0 +1,56 @@ +#include "Dictionary.h" + +bool Dictionary::HasKey(const std::string& key) const +{ + return HasStringKey(key) || HasDictKey(key); +} + +bool Dictionary::HasStringKey(const std::string& key) const +{ + return mStringData.count(key) > 0; +} + +bool Dictionary::HasDictKey(const std::string& key) const +{ + return mDictData.count(key) > 0; +} + +std::vector Dictionary::GetStringKeys() const +{ + std::vector keys; + for (const auto& item : mStringData) + { + keys.push_back(item.first); + } + return keys; +} + +std::vector Dictionary::GetDictKeys() const +{ + std::vector keys; + for (const auto& item : mDictData) + { + keys.push_back(item.first); + } + return keys; +} + +Dictionary* Dictionary::GetDict(const std::string& key) const +{ + return mDictData.at(key).get(); +} + +std::string Dictionary::GetItem(const std::string& key) const +{ + return mStringData.at(key); +} + +void Dictionary::AddStringItem(const std::string& key, const std::string& item) +{ + mStringData[key] = item; +} + +void Dictionary::AddDictItem(const std::string& key, std::unique_ptr dict) +{ + mDictData[key] = std::move(dict); +} diff --git a/src/core/Dictionary.h b/src/core/Dictionary.h new file mode 100644 index 0000000..8399608 --- /dev/null +++ b/src/core/Dictionary.h @@ -0,0 +1,35 @@ +#pragma once +#include +#include +#include +#include + +class Dictionary +{ +public: + Dictionary() = default; + virtual ~Dictionary() = default; + + bool HasKey(const std::string& key) const; + + bool HasStringKey(const std::string& key) const; + + bool HasDictKey(const std::string& key) const; + + Dictionary* GetDict(const std::string& key) const; + + std::vector GetDictKeys() const; + + std::vector GetStringKeys() const; + + std::string GetItem(const std::string& key) const; + + void AddStringItem(const std::string& key, const std::string& item); + + void AddDictItem(const std::string& key, std::unique_ptr dict); + +protected: + + std::map mStringData; + std::map > mDictData; +}; diff --git a/src/core/StringUtils.cpp b/src/core/StringUtils.cpp index 547f2a7..da21622 100644 --- a/src/core/StringUtils.cpp +++ b/src/core/StringUtils.cpp @@ -1,6 +1,8 @@ #include "StringUtils.h" #include #include +#include +#include #ifdef _WIN32 #include "Windows.h" @@ -45,3 +47,10 @@ std::string StringUtils::convert(const std::wstring& input) throw std::logic_error("Not implemented"); #endif } + +std::string StringUtils::ToPaddedString(unsigned numBytes, unsigned entry) +{ + std::stringstream sstr; + sstr << std::setfill('0') << std::setw(numBytes) << entry; + return sstr.str(); +} diff --git a/src/core/StringUtils.h b/src/core/StringUtils.h index 2a70f79..fff9b36 100644 --- a/src/core/StringUtils.h +++ b/src/core/StringUtils.h @@ -18,4 +18,5 @@ public: static bool IsSpace(char c); static std::string ToLower(const std::string& s); static std::string convert(const std::wstring& input); + static std::string ToPaddedString(unsigned numBytes, unsigned entry); }; diff --git a/src/database/DatabaseManager.cpp b/src/database/DatabaseManager.cpp index e89653b..025f790 100644 --- a/src/database/DatabaseManager.cpp +++ b/src/database/DatabaseManager.cpp @@ -27,6 +27,11 @@ void DatabaseManager::CreateDatabase(const std::string& path) mDatabaseInterface->Open(mDatabase); } +void DatabaseManager::Run(const std::string& statement) +{ + mDatabaseInterface->Run(statement); +} + void DatabaseManager::OnShutDown() { if(mDatabaseInterface) diff --git a/src/database/DatabaseManager.h b/src/database/DatabaseManager.h index fa156fa..239763e 100644 --- a/src/database/DatabaseManager.h +++ b/src/database/DatabaseManager.h @@ -7,10 +7,6 @@ class DatabaseManager { - - DatabasePtr mDatabase; - SqliteInterfacePtr mDatabaseInterface; - public: DatabaseManager(); @@ -21,7 +17,13 @@ public: void CreateDatabase(const std::string& path); + void Run(const std::string& statement); + void OnShutDown(); + +private: + DatabasePtr mDatabase; + SqliteInterfacePtr mDatabaseInterface; }; using DatabaseManagerPtr = std::unique_ptr; diff --git a/src/image/CMakeLists.txt b/src/image/CMakeLists.txt new file mode 100644 index 0000000..d9b9bd4 --- /dev/null +++ b/src/image/CMakeLists.txt @@ -0,0 +1,21 @@ +list(APPEND image_HEADERS + Image.h + PngWriter.h + ) + +list(APPEND image_LIB_INCLUDES + Image.cpp + PngWriter.cpp + ) + +add_library(image SHARED ${image_LIB_INCLUDES} ${image_HEADERS}) + +target_include_directories(image PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" + ) +set_target_properties( image PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON ) + +find_package(PNG REQUIRED) +target_link_libraries( image PUBLIC PNG::PNG) + +set_property(TARGET image PROPERTY FOLDER src) \ No newline at end of file diff --git a/src/image/Image.cpp b/src/image/Image.cpp new file mode 100644 index 0000000..68a821d --- /dev/null +++ b/src/image/Image.cpp @@ -0,0 +1,69 @@ +#include "Image.h" + +Image::Image(unsigned width, unsigned height) + : mWidth(width), + mHeight(height) +{ + +} + +std::unique_ptr Image::Create(unsigned width, unsigned height) +{ + return std::make_unique(width, height); +} + +unsigned Image::GetBytesPerRow() const +{ + const auto bitsPerEntry = mBitDepth <= 8 ? 1 : 2; + return mWidth * mNumChannels *bitsPerEntry; +} + +unsigned Image::GetWidth() const +{ + return mWidth; +} + +unsigned Image::GetHeight() const +{ + return mHeight; +} + +unsigned Image::GetBitDepth() const +{ + return mBitDepth; +} + +unsigned char Image::GetByte(unsigned idx, unsigned jdx) const +{ + return mData[jdx*GetBytesPerRow() + idx]; +} + +unsigned Image::GetNumChannels() const +{ + return mNumChannels; +} + +void Image::SetData(const std::vector& data) +{ + mData = data; +} + +void Image::SetWidth(unsigned width) +{ + mWidth = width; +} + +void Image::SetHeight(unsigned height) +{ + mHeight = height; +} + +void Image::SetBitDepth(unsigned bitDepth) +{ + mBitDepth = bitDepth; +} + +void Image::SetNumChannels(unsigned numChannels) +{ + mNumChannels = numChannels; +} diff --git a/src/image/Image.h b/src/image/Image.h new file mode 100644 index 0000000..a19241f --- /dev/null +++ b/src/image/Image.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + + +class Image +{ +public: + + Image(unsigned width, unsigned height); + static std::unique_ptr Create(unsigned width, unsigned height); + + unsigned GetBytesPerRow() const; + unsigned GetWidth() const; + unsigned GetHeight() const; + unsigned GetBitDepth() const; + unsigned GetNumChannels() const; + unsigned char GetByte(unsigned idx, unsigned jdx) const; + + void SetData(const std::vector& data); + void SetWidth(unsigned width); + void SetHeight(unsigned height); + void SetBitDepth(unsigned bitDepth); + void SetNumChannels(unsigned numChannels); + +private: + unsigned mWidth{1}; + unsigned mHeight{1}; + unsigned mBitDepth{8}; + unsigned mNumChannels{1}; + std::vector mData; +}; + +using ImagePtr = std::unique_ptr; diff --git a/src/image/PngWriter.cpp b/src/image/PngWriter.cpp new file mode 100644 index 0000000..5650573 --- /dev/null +++ b/src/image/PngWriter.cpp @@ -0,0 +1,74 @@ +#include "PngWriter.h" + +#include "Image.h" + +#include +#include + +std::unique_ptr PngWriter::Create() +{ + return std::make_unique(); +} + +void PngWriter::SetPath(const std::string& path) +{ + mPath = path; +} + +void PngWriter::Write(const std::unique_ptr& image) const +{ + auto fp = fopen(mPath.c_str(), "wb"); + + auto png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + auto info_ptr = png_create_info_struct(png_ptr); + + if (setjmp(png_jmpbuf(png_ptr))) + { + return; + } + png_init_io(png_ptr, fp); + + + if (setjmp(png_jmpbuf(png_ptr))) + { + return; + } + auto color_type = PNG_COLOR_TYPE_RGB; + png_set_IHDR(png_ptr, info_ptr, image->GetWidth(), image->GetHeight(), + image->GetBitDepth(), color_type, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + png_write_info(png_ptr, info_ptr); + + if (setjmp(png_jmpbuf(png_ptr))) + { + return; + } + + png_bytep* row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * image->GetHeight()); + auto row_size = image->GetBytesPerRow(); + for(unsigned jdx=0;jdxGetHeight();jdx++) + { + row_pointers[jdx]=(png_byte*)malloc(sizeof(png_byte)*row_size); + for(unsigned idx=0;idxGetByte(idx, jdx); + } + } + png_write_image(png_ptr, row_pointers); + if (setjmp(png_jmpbuf(png_ptr))) + { + return; + } + + png_write_end(png_ptr, nullptr); + + for (unsigned y=0; yGetHeight(); y++) + { + free(row_pointers[y]); + } + free(row_pointers); + + fclose(fp); + return; +} diff --git a/src/image/PngWriter.h b/src/image/PngWriter.h new file mode 100644 index 0000000..a31752d --- /dev/null +++ b/src/image/PngWriter.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +class Image; + +class PngWriter +{ +public: + static std::unique_ptr Create(); + + void SetPath(const std::string& path); + + void Write(const std::unique_ptr& image) const; + +private: + + std::string mPath; +}; + +using PngWriterPtr = std::unique_ptr; diff --git a/src/network/NetworkManager.h b/src/network/NetworkManager.h index 0e6c99d..bd4e05d 100644 --- a/src/network/NetworkManager.h +++ b/src/network/NetworkManager.h @@ -8,9 +8,6 @@ class NetworkManager { - std::vector mActiveSockets; - ISocketInterfaceUPtr mSocketInterface; - public: NetworkManager(); @@ -24,6 +21,10 @@ public: void RunHttpServer(); void ShutDown(); + +private: + std::vector mActiveSockets; + ISocketInterfaceUPtr mSocketInterface; }; using NetworkManagerUPtr = std::unique_ptr; diff --git a/src/network/sockets/Socket.h b/src/network/sockets/Socket.h index 9bc421b..f6756e1 100644 --- a/src/network/sockets/Socket.h +++ b/src/network/sockets/Socket.h @@ -29,7 +29,7 @@ public: private: SocketHandle mHandle; - unsigned mPort; + unsigned mPort{0}; std::string mMessage; }; diff --git a/src/publishing/CMakeLists.txt b/src/publishing/CMakeLists.txt new file mode 100644 index 0000000..a962bab --- /dev/null +++ b/src/publishing/CMakeLists.txt @@ -0,0 +1,36 @@ +list(APPEND publishing_HEADERS + pdf/PdfDocument.h + pdf/PdfDocumentCatalog.h + pdf/PdfDictionary.h + pdf/PdfObject.h + pdf/PdfOutline.h + pdf/PdfPageTree.h + pdf/PdfPage.h + pdf/PdfStream.h + pdf/PdfXRefTable.h + pdf/PdfWriter.h + ) + +list(APPEND publishing_LIB_INCLUDES + pdf/PdfDocument.cpp + pdf/PdfDocumentCatalog.cpp + pdf/PdfDictionary.cpp + pdf/PdfObject.cpp + pdf/PdfOutline.cpp + pdf/PdfPageTree.cpp + pdf/PdfPage.cpp + pdf/PdfStream.cpp + pdf/PdfXRefTable.cpp + pdf/PdfWriter.cpp + ) + +add_library(publishing SHARED ${publishing_LIB_INCLUDES} ${publishing_INCLUDES} ${publishing_HEADERS}) + +target_include_directories(publishing PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" + "${CMAKE_CURRENT_SOURCE_DIR}/pdf" + ) +set_target_properties( publishing PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON ) +target_link_libraries( publishing PUBLIC core) + +set_property(TARGET publishing PROPERTY FOLDER src) \ No newline at end of file diff --git a/src/publishing/pdf/PdfDictionary.cpp b/src/publishing/pdf/PdfDictionary.cpp new file mode 100644 index 0000000..d5da237 --- /dev/null +++ b/src/publishing/pdf/PdfDictionary.cpp @@ -0,0 +1,45 @@ +#include "PdfDictionary.h" + +std::string PdfDictionary::ToString() const +{ + const auto keys = GetStringKeys(); + if (keys.empty()) + { + return ""; + } + + std::string content = " <<"; + bool first = true; + auto ending = keys.size() == 1 ? "" : "\n"; + for (const auto& key : keys) + { + if (first) + { + content += " /" + key + " " + GetItem(key) + ending; + first = false; + } + else + { + content += " /" + key + " " + GetItem(key) + ending; + } + } + + const auto dictKeys = GetDictKeys(); + ending = dictKeys.size() == 1 ? "" : "\n"; + for (const auto& key : GetDictKeys()) + { + auto pdfDict = dynamic_cast(GetDict(key)); + if (first) + { + content += " /" + key + " " + pdfDict->ToString() + ending; + first = false; + } + else + { + content += " /" + key + " " + pdfDict->ToString() + ending; + } + } + + content += " >>\n"; + return content; +} diff --git a/src/publishing/pdf/PdfDictionary.h b/src/publishing/pdf/PdfDictionary.h new file mode 100644 index 0000000..9987ac8 --- /dev/null +++ b/src/publishing/pdf/PdfDictionary.h @@ -0,0 +1,10 @@ +#pragma once + +#include "Dictionary.h" + +class PdfDictionary : public Dictionary +{ +public: + + std::string ToString() const; +}; diff --git a/src/publishing/pdf/PdfDocument.cpp b/src/publishing/pdf/PdfDocument.cpp new file mode 100644 index 0000000..eb18bea --- /dev/null +++ b/src/publishing/pdf/PdfDocument.cpp @@ -0,0 +1,73 @@ +#include "PdfDocument.h" + +#include "PdfObject.h" +#include "PdfDocumentCatalog.h" +#include "PdfPageTree.h" +#include "PdfPage.h" +#include "PdfOutline.h" +#include "PdfXRefTable.h" + +PdfDocument::PdfDocument() +{ + mXRefTable = std::make_unique(); + mCatalog = std::make_unique(); +} + +PdfDocument::~PdfDocument() +{ + +} + +std::string PdfDocument::ToString() +{ + IndexObjects(); + auto content = GetHeaderString(); + content += GetBodyString(); + + content += GetXRefString(); + + content += GetTrailerString(); + + content += "%%EOF\n"; + return content; +} + +std::string PdfDocument::GetHeaderString() +{ + auto content = "%PDF-" + mVersion + "\n"; + mXRefOffset = content.size(); + mXRefTable->AddRecord(content.size(), 65535, true); + return content; +} + +std::string PdfDocument::GetBodyString() +{ + const auto content = mCatalog->ToString(mXRefTable.get()); + mXRefOffset += content.size(); + return content; +} + +std::string PdfDocument::GetXRefString() +{ + return mXRefTable->ToString(); +} + +std::string PdfDocument::GetTrailerString() +{ + const auto numObjects = mXRefTable->GetNumEntries(); + mTrailer.AddStringItem("Size", std::to_string(numObjects)); + mTrailer.AddStringItem("Root", mCatalog->GetRefString()); + + std::string content = "trailer\n"; + content += mTrailer.ToString(); + + content += "startxref\n"; + content += std::to_string(mXRefOffset) + "\n"; + return content; +} + +void PdfDocument::IndexObjects() +{ + mCatalog->IndexObjects(0); +} + diff --git a/src/publishing/pdf/PdfDocument.h b/src/publishing/pdf/PdfDocument.h new file mode 100644 index 0000000..5139c0d --- /dev/null +++ b/src/publishing/pdf/PdfDocument.h @@ -0,0 +1,44 @@ +#pragma once + +#include "PdfDictionary.h" +#include "StringUtils.h" + +#include +#include +#include + +class PdfDocumentCatalog; +using PdfDocumentCatalogPtr = std::unique_ptr; + +class PdfXRefTable; +using PdfXRefTablePtr = std::unique_ptr; + +class PdfDocument +{ + +public: + PdfDocument(); + ~PdfDocument(); + + std::string ToString(); + +private: + std::string GetHeaderString(); + + std::string GetTrailerString(); + + std::string GetBodyString(); + + std::string GetXRefString(); + + void IndexObjects(); + +private: + PdfDictionary mTrailer; + std::string mVersion {"1.7"}; + PdfXRefTablePtr mXRefTable; + unsigned mXRefOffset{0}; + PdfDocumentCatalogPtr mCatalog; +}; + +using PdfDocumentPtr = std::unique_ptr; diff --git a/src/publishing/pdf/PdfDocumentCatalog.cpp b/src/publishing/pdf/PdfDocumentCatalog.cpp new file mode 100644 index 0000000..6a9bdd3 --- /dev/null +++ b/src/publishing/pdf/PdfDocumentCatalog.cpp @@ -0,0 +1,44 @@ +#include "PdfDocumentCatalog.h" + +#include "PdfOutline.h" +#include "PdfPageTree.h" +#include "PdfPage.h" +#include "PdfXRefTable.h" + +PdfDocumentCatalog::PdfDocumentCatalog() + : PdfObject() +{ + mOutlines = std::make_unique(); + mPages = std::make_unique(); +} + +unsigned PdfDocumentCatalog::IndexObjects(unsigned count) +{ + auto newCount = count + 1; + mObjectNumber = newCount; + auto objectCount = mOutlines->IndexObjects(mObjectNumber); + objectCount = mPages->IndexObjects(objectCount); + return objectCount; +} + +std::string PdfDocumentCatalog::ToString(PdfXRefTable* xRefTable) +{ + UpdateDictionary(); + auto content = GetStringPrefix(); + content += mDictionary.ToString(); + content += GetStringSuffix(); + xRefTable->AddRecord(content.size(), mGenerationNumber, mIsFree); + + content += mOutlines->ToString(xRefTable); + + content += mPages->ToString(xRefTable); + + return content; +} + +void PdfDocumentCatalog::UpdateDictionary() +{ + mDictionary.AddStringItem("Type", "/Catalog"); + mDictionary.AddStringItem("Outlines", mOutlines->GetRefString()); + mDictionary.AddStringItem("Pages", mPages->GetRefString()); +} diff --git a/src/publishing/pdf/PdfDocumentCatalog.h b/src/publishing/pdf/PdfDocumentCatalog.h new file mode 100644 index 0000000..d2665d6 --- /dev/null +++ b/src/publishing/pdf/PdfDocumentCatalog.h @@ -0,0 +1,23 @@ +#pragma once + +#include "PdfObject.h" + +class PdfPageTree; +class PdfOutlineCollection; + +class PdfDocumentCatalog : public PdfObject +{ +public: + + PdfDocumentCatalog(); + + unsigned IndexObjects(unsigned count) override; + + std::string ToString(PdfXRefTable* xRefTable) override; + +private: + void UpdateDictionary() override; + + std::unique_ptr mOutlines; + std::unique_ptr mPages; +}; diff --git a/src/publishing/pdf/PdfObject.cpp b/src/publishing/pdf/PdfObject.cpp new file mode 100644 index 0000000..26ba0ca --- /dev/null +++ b/src/publishing/pdf/PdfObject.cpp @@ -0,0 +1,58 @@ +#include "PdfObject.h" + +#include "PdfDictionary.h" +#include "PdfXRefTable.h" + +std::string PdfObject::ToString(PdfXRefTable* xRefTable) +{ + UpdateDictionary(); + + auto content = GetStringPrefix(); + content += mDictionary.ToString(); + content += GetStringSuffix(); + + xRefTable->AddRecord(content.size(), mGenerationNumber, mIsFree); + return content; +} + +void PdfObject::UpdateDictionary() +{ + +} + +std::string PdfObject::GetStringSuffix() const +{ + return "endobj\n\n"; +} + +unsigned PdfObject::IndexObjects(unsigned count) +{ + const auto newCount = count + 1; + mObjectNumber = newCount; + return newCount; +} + +void PdfObject::SetObjectNumber(unsigned num) +{ + mObjectNumber = num; +} + +void PdfObject::SetGenerationNumber(unsigned num) +{ + mGenerationNumber = num; +} + +std::string PdfObject::GetStringPrefix() const +{ + return std::to_string(mObjectNumber) + " " + std::to_string(mGenerationNumber) + " obj\n"; +} + +std::string PdfObject::GetRefString() const +{ + return std::to_string(mObjectNumber) + " " + std::to_string(mGenerationNumber) + " R"; +} + +bool PdfObject::IsFree() const +{ + return mIsFree; +} diff --git a/src/publishing/pdf/PdfObject.h b/src/publishing/pdf/PdfObject.h new file mode 100644 index 0000000..67be2c6 --- /dev/null +++ b/src/publishing/pdf/PdfObject.h @@ -0,0 +1,40 @@ +#pragma once + +#include "PdfDictionary.h" + +#include +#include + +class PdfXRefTable; + +class PdfObject +{ +public: + virtual ~PdfObject() = default; + + std::string GetStringPrefix() const; + + std::string GetStringSuffix() const; + + std::string GetRefString() const; + + virtual unsigned IndexObjects(unsigned count); + + bool IsFree() const; + + virtual std::string ToString(PdfXRefTable* xRefTable); + + void SetObjectNumber(unsigned num); + + void SetGenerationNumber(unsigned num); + +protected: + + virtual void UpdateDictionary(); + + unsigned mObjectNumber{0}; + unsigned mGenerationNumber{0}; + PdfDictionary mDictionary; + bool mIsFree {false}; +}; +using PdfObjectPtr = std::unique_ptr; diff --git a/src/publishing/pdf/PdfOutline.cpp b/src/publishing/pdf/PdfOutline.cpp new file mode 100644 index 0000000..5b83595 --- /dev/null +++ b/src/publishing/pdf/PdfOutline.cpp @@ -0,0 +1,40 @@ +#include "PdfOutline.h" + +#include "PdfXRefTable.h" + +std::string PdfOutline::ToString(PdfXRefTable* xRefTable) +{ + mDictionary.AddStringItem("Type", "/Outline"); + auto content = GetStringPrefix(); + content += mDictionary.ToString(); + content += GetStringSuffix(); + xRefTable->AddRecord(content.size(), mGenerationNumber, mIsFree); + return content; +} + +unsigned PdfOutlineCollection::IndexObjects(unsigned count) +{ + auto newCount = count + 1; + mObjectNumber = newCount; + for (const auto& outline : mOutlines) + { + newCount = outline->IndexObjects(newCount); + } + return newCount; +} + +std::string PdfOutlineCollection::ToString(PdfXRefTable* xRefTable) +{ + UpdateDictionary(); + std::string content = GetStringPrefix(); + content += mDictionary.ToString(); + content += GetStringSuffix(); + xRefTable->AddRecord(content.size(), mGenerationNumber, mIsFree); + return content; +} + +void PdfOutlineCollection::UpdateDictionary() +{ + mDictionary.AddStringItem("Type", "/Outlines"); + mDictionary.AddStringItem("Count", std::to_string(mOutlines.size())); +} diff --git a/src/publishing/pdf/PdfOutline.h b/src/publishing/pdf/PdfOutline.h new file mode 100644 index 0000000..1d5e257 --- /dev/null +++ b/src/publishing/pdf/PdfOutline.h @@ -0,0 +1,26 @@ +#pragma once + +#include "PdfObject.h" + +#include + +class PdfOutline : public PdfObject +{ +public: + std::string ToString(PdfXRefTable* xRefTable) override; +}; +using PdfOutlinePtr = std::unique_ptr; + +class PdfOutlineCollection : public PdfObject +{ +public: + + unsigned IndexObjects(unsigned count) override; + + std::string ToString(PdfXRefTable* xRefTable) override; + + void UpdateDictionary(); + +private: + std::vector mOutlines; +}; diff --git a/src/publishing/pdf/PdfPage.cpp b/src/publishing/pdf/PdfPage.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/publishing/pdf/PdfPage.h b/src/publishing/pdf/PdfPage.h new file mode 100644 index 0000000..e022c76 --- /dev/null +++ b/src/publishing/pdf/PdfPage.h @@ -0,0 +1,121 @@ +#pragma once + +#include "PdfObject.h" +#include "PdfStream.h" +#include "PdfXRefTable.h" + +class PdfPageTree; + +class PdfProcSet : public PdfObject +{ +public: + std::string ToString(PdfXRefTable* xRefTable) override + { + UpdateDictionary(); + auto content = GetStringPrefix(); + content += mDictionary.ToString(); + content += "[/PDF]\n"; + content += GetStringSuffix(); + xRefTable->AddRecord(content.size(), mGenerationNumber, mIsFree); + + return content; + } +}; + +class PdfFont : public PdfObject +{ +public: + std::string ToString(PdfXRefTable* xRefTable) override + { + UpdateDictionary(); + auto content = GetStringPrefix(); + content += mDictionary.ToString(); + content += GetStringSuffix(); + xRefTable->AddRecord(content.size(), mGenerationNumber, mIsFree); + return content; + } + + void UpdateDictionary() + { + mDictionary.AddStringItem("Type", "/Font"); + mDictionary.AddStringItem("Subtype", "/Type1"); + mDictionary.AddStringItem("Name", "/F1"); + mDictionary.AddStringItem("BaseFont", "/Helvetica"); + mDictionary.AddStringItem("Encoding", "/MacRomanEncoding"); + } +}; + +class PdfPage : public PdfObject +{ +public: + PdfPage(PdfPageTree* parent) + : mParent(parent) + { + mContent = std::make_unique(); + + std::string pageContent = "BT\n"; + pageContent += "/F1 24 Tf\n"; + pageContent += "100 100 Td\n"; + pageContent += "(Hello World) Tj\n"; + pageContent += "ET"; + + mContent->SetContent(pageContent); + mProcSet = std::make_unique(); + + mDefaultFont = std::make_unique(); + } + + unsigned IndexObjects(unsigned count) + { + auto newCount = count + 1; + mObjectNumber = newCount; + newCount = mContent->IndexObjects(newCount); + newCount = mProcSet->IndexObjects(newCount); + newCount = mDefaultFont->IndexObjects(newCount); + return newCount; + } + + std::string ToString(PdfXRefTable* xRefTable) override + { + UpdateDictionary(); + auto content = GetStringPrefix(); + content += mDictionary.ToString(); + content += GetStringSuffix(); + xRefTable->AddRecord(content.size(), mGenerationNumber, mIsFree); + + content += mContent->ToString(xRefTable); + + content += mProcSet->ToString(xRefTable); + + content += mDefaultFont->ToString(xRefTable); + + return content; + } + + void UpdateDictionary() + { + std::string mediaBox = "[0 0 " + std::to_string(mWidth) + " " + std::to_string(mHeight) + "]"; + mDictionary.AddStringItem("Type", "/Page"); + mDictionary.AddStringItem("MediaBox", mediaBox); + mDictionary.AddStringItem("Parent", mParent->GetRefString()); + mDictionary.AddStringItem("Contents", mContent->GetRefString()); + + auto resourcesDict = std::make_unique(); + resourcesDict->AddStringItem("ProcSet", mProcSet->GetRefString()); + + auto fontDict = std::make_unique(); + fontDict->AddStringItem("F1", mDefaultFont->GetRefString()); + resourcesDict->AddDictItem("Font", std::move(fontDict)); + + mDictionary.AddDictItem("Resources", std::move(resourcesDict)); + } + +private: + unsigned mWidth{612}; + unsigned mHeight{792}; + std::unique_ptr mContent; + std::unique_ptr mDefaultFont; + PdfObjectPtr mProcSet; + PdfPageTree* mParent; + +}; diff --git a/src/publishing/pdf/PdfPageTree.cpp b/src/publishing/pdf/PdfPageTree.cpp new file mode 100644 index 0000000..c62b946 --- /dev/null +++ b/src/publishing/pdf/PdfPageTree.cpp @@ -0,0 +1,37 @@ +#include "PdfPageTree.h" + +#include "PdfXRefTable.h" +#include "PdfPage.h" + +PdfPageTree::PdfPageTree() +{ + mRootPage = std::make_unique(this); +} + +unsigned PdfPageTree::IndexObjects(unsigned count) +{ + auto newCount = count + 1; + mObjectNumber = newCount; + newCount = mRootPage->IndexObjects(newCount); + return newCount; +} + +std::string PdfPageTree::ToString(PdfXRefTable* xRefTable) +{ + UpdateDictionary(); + std::string content = GetStringPrefix(); + content += mDictionary.ToString(); + content += GetStringSuffix(); + xRefTable->AddRecord(content.size(), mGenerationNumber, mIsFree); + + content += mRootPage->ToString(xRefTable); + return content; +} + +void PdfPageTree::UpdateDictionary() +{ + mDictionary.AddStringItem("Type", "/Pages"); + std::string kids = "[" + mRootPage->GetRefString() + "]"; + mDictionary.AddStringItem("Kids", kids); + mDictionary.AddStringItem("Count", "1"); +} diff --git a/src/publishing/pdf/PdfPageTree.h b/src/publishing/pdf/PdfPageTree.h new file mode 100644 index 0000000..897db9b --- /dev/null +++ b/src/publishing/pdf/PdfPageTree.h @@ -0,0 +1,23 @@ +#pragma once + +#include "PdfObject.h" + +class PdfPage; +using PdfPagePtr = std::unique_ptr; + +class PdfPageTree : public PdfObject +{ +public: + + PdfPageTree(); + + unsigned IndexObjects(unsigned count) override; + + std::string ToString(PdfXRefTable* xRefTable) override; + + void UpdateDictionary() override; + +private: + + PdfPagePtr mRootPage; +}; diff --git a/src/publishing/pdf/PdfStream.cpp b/src/publishing/pdf/PdfStream.cpp new file mode 100644 index 0000000..0fc34f7 --- /dev/null +++ b/src/publishing/pdf/PdfStream.cpp @@ -0,0 +1,36 @@ +#include "PdfStream.h" + +#include "PdfDictionary.h" +#include "PdfXRefTable.h" + +PdfStream::PdfStream() +{ + mDictionary.AddStringItem("Length", "0"); +} + +void PdfStream::SetContent(const std::string& content) +{ + mContent = content; + Update(); +} + +void PdfStream::Update() +{ + auto length = mContent.size(); + mDictionary.AddStringItem("Length", std::to_string(length)); +} + +std::string PdfStream::ToString(PdfXRefTable* xRefTable) +{ + std::string content = GetStringPrefix(); + + content += mDictionary.ToString(); + + content += "stream\n"; + content += mContent; + content += "\nendstream\n"; + content += "endobj\n\n"; + + xRefTable->AddRecord(content.size(), mGenerationNumber, mIsFree); + return content; +} diff --git a/src/publishing/pdf/PdfStream.h b/src/publishing/pdf/PdfStream.h new file mode 100644 index 0000000..595dc5a --- /dev/null +++ b/src/publishing/pdf/PdfStream.h @@ -0,0 +1,19 @@ +#pragma once + +#include "PdfObject.h" + +class PdfStream : public PdfObject +{ +public: + + PdfStream(); + + void Update(); + + void SetContent(const std::string& content); + + std::string ToString(PdfXRefTable* xRefTable) override; + +private: + std::string mContent; +}; diff --git a/src/publishing/pdf/PdfWriter.cpp b/src/publishing/pdf/PdfWriter.cpp new file mode 100644 index 0000000..2735658 --- /dev/null +++ b/src/publishing/pdf/PdfWriter.cpp @@ -0,0 +1,12 @@ +#include "PdfWriter.h" + +#include "PdfDocument.h" + +std::string PdfWriter::ToString(const std::unique_ptr& document) const +{ + std::string content; + + content += document->ToString(); + + return content; +} diff --git a/src/publishing/pdf/PdfWriter.h b/src/publishing/pdf/PdfWriter.h new file mode 100644 index 0000000..0b03765 --- /dev/null +++ b/src/publishing/pdf/PdfWriter.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +class PdfDocument; + +class PdfWriter +{ +public: + + std::string ToString(const std::unique_ptr& document) const; +}; diff --git a/src/publishing/pdf/PdfXRefTable.cpp b/src/publishing/pdf/PdfXRefTable.cpp new file mode 100644 index 0000000..827ce62 --- /dev/null +++ b/src/publishing/pdf/PdfXRefTable.cpp @@ -0,0 +1,68 @@ +#include "PdfXRefTable.h" + +#include "StringUtils.h" + +PdfXRefTable::PdfXRefTable() +{ + mSections.push_back(TableSubSection()); +} + +std::string PdfXRefTable::ToString() +{ + std::string content; + for (const auto& section : mSections) + { + content += "xref\n" + std::to_string(section.mStartIndex) + " " + std::to_string(section.mRecords.size()); + content += "\n"; + for (const auto& record : section.mRecords) + { + auto offsetString = StringUtils::ToPaddedString(10, record.mOffsetBytes); + auto generationString = StringUtils::ToPaddedString(5, record.mGenerationNumber); + auto freeString = record.mIsFree ? "f" : "n"; + + content += offsetString + " " + generationString + " " + freeString + "\n"; + } + content += "\n"; + } + return content; +} + +unsigned PdfXRefTable::GetNextOffset() +{ + auto lastNumRecords = mSections[mSections.size() - 1].mRecords.size(); + if (lastNumRecords > 0) + { + return mSections[mSections.size() - 1].mRecords[lastNumRecords -1].mOffsetBytes + mLastAddedBytes; + } + else if (mSections.size() > 1) + { + lastNumRecords = mSections[mSections.size() - 2].mRecords.size(); + return mSections[mSections.size() - 2].mRecords[lastNumRecords -1].mOffsetBytes + mLastAddedBytes; + } + else + { + return 0; + } +} + +void PdfXRefTable::AddRecord(unsigned numBytes, unsigned generation, unsigned isFree) +{ + XRefRecord record; + + record.mOffsetBytes = GetNextOffset(); + record.mGenerationNumber = generation; + record.mIsFree = isFree; + mSections[mSections.size()-1].mRecords.push_back(record); + mLastAddedBytes = numBytes; +} + +unsigned PdfXRefTable::GetNumEntries() +{ + unsigned count = 0; + for (const auto& section : mSections) + { + count += section.mRecords.size(); + } + return count; +} + diff --git a/src/publishing/pdf/PdfXRefTable.h b/src/publishing/pdf/PdfXRefTable.h new file mode 100644 index 0000000..9bd8a51 --- /dev/null +++ b/src/publishing/pdf/PdfXRefTable.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include + +struct XRefRecord +{ + unsigned mOffsetBytes{0}; + unsigned mGenerationNumber{0}; + bool mIsFree{false}; +}; + +struct TableSubSection +{ + unsigned mStartIndex{0}; + std::vector mRecords; +}; + +class PdfXRefTable +{ +public: + + PdfXRefTable(); + + std::string ToString(); + + unsigned GetNextOffset(); + + void AddRecord(unsigned numBytes, unsigned generation, unsigned isFree); + + unsigned GetNumEntries(); + +private: + unsigned mLastAddedBytes{0}; + std::vector mSections; +}; diff --git a/src/video/CMakeLists.txt b/src/video/CMakeLists.txt new file mode 100644 index 0000000..d1683d0 --- /dev/null +++ b/src/video/CMakeLists.txt @@ -0,0 +1,30 @@ +list(APPEND video_HEADERS + Video.h + FfmpegInterface.h + ) + +list(APPEND video_LIB_INCLUDES + Video.cpp + FfmegInterface.cpp + ) + +add_library(video SHARED ${video_LIB_INCLUDES} ${video_HEADERS}) + +target_include_directories(video PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" + ) +set_target_properties( video PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON ) + +find_package(PkgConfig REQUIRED) +pkg_check_modules(LIBAV REQUIRED IMPORTED_TARGET + libavdevice + libavfilter + libavformat + libavcodec + libswresample + libswscale + libavutil +) +target_link_libraries( video PUBLIC image PkgConfig::LIBAV) + +set_property(TARGET image PROPERTY FOLDER src) \ No newline at end of file diff --git a/src/video/FfmegInterface.cpp b/src/video/FfmegInterface.cpp new file mode 100644 index 0000000..3356300 --- /dev/null +++ b/src/video/FfmegInterface.cpp @@ -0,0 +1,118 @@ +#include "FfmpegInterface.h" + +#include "Image.h" +#include "Video.h" +#include + +extern "C" { +#include +#include +#include +#include +#include +} + +std::vector > FfmpegInterface::decodeToImages(const std::unique_ptr