Add PDF writer.
This commit is contained in:
parent
c05b7b6315
commit
9c116b1efd
72 changed files with 1819 additions and 114 deletions
|
@ -26,14 +26,5 @@ 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)
|
|
@ -1,38 +0,0 @@
|
|||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#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;
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
56
src/core/Dictionary.cpp
Normal file
56
src/core/Dictionary.cpp
Normal file
|
@ -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<std::string> Dictionary::GetStringKeys() const
|
||||
{
|
||||
std::vector<std::string> keys;
|
||||
for (const auto& item : mStringData)
|
||||
{
|
||||
keys.push_back(item.first);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
std::vector<std::string> Dictionary::GetDictKeys() const
|
||||
{
|
||||
std::vector<std::string> 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<Dictionary> dict)
|
||||
{
|
||||
mDictData[key] = std::move(dict);
|
||||
}
|
35
src/core/Dictionary.h
Normal file
35
src/core/Dictionary.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
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<std::string> GetDictKeys() const;
|
||||
|
||||
std::vector<std::string> 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<Dictionary> dict);
|
||||
|
||||
protected:
|
||||
|
||||
std::map<std::string, std::string> mStringData;
|
||||
std::map<std::string, std::unique_ptr<Dictionary> > mDictData;
|
||||
};
|
|
@ -1,6 +1,8 @@
|
|||
#include "StringUtils.h"
|
||||
#include <locale>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
#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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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<DatabaseManager>;
|
||||
|
|
21
src/image/CMakeLists.txt
Normal file
21
src/image/CMakeLists.txt
Normal file
|
@ -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)
|
69
src/image/Image.cpp
Normal file
69
src/image/Image.cpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
#include "Image.h"
|
||||
|
||||
Image::Image(unsigned width, unsigned height)
|
||||
: mWidth(width),
|
||||
mHeight(height)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::unique_ptr<Image> Image::Create(unsigned width, unsigned height)
|
||||
{
|
||||
return std::make_unique<Image>(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<unsigned char>& 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;
|
||||
}
|
35
src/image/Image.h
Normal file
35
src/image/Image.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
|
||||
class Image
|
||||
{
|
||||
public:
|
||||
|
||||
Image(unsigned width, unsigned height);
|
||||
static std::unique_ptr<Image> 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<unsigned char>& 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<unsigned char> mData;
|
||||
};
|
||||
|
||||
using ImagePtr = std::unique_ptr<Image>;
|
74
src/image/PngWriter.cpp
Normal file
74
src/image/PngWriter.cpp
Normal file
|
@ -0,0 +1,74 @@
|
|||
#include "PngWriter.h"
|
||||
|
||||
#include "Image.h"
|
||||
|
||||
#include <png.h>
|
||||
#include <stdio.h>
|
||||
|
||||
std::unique_ptr<PngWriter> PngWriter::Create()
|
||||
{
|
||||
return std::make_unique<PngWriter>();
|
||||
}
|
||||
|
||||
void PngWriter::SetPath(const std::string& path)
|
||||
{
|
||||
mPath = path;
|
||||
}
|
||||
|
||||
void PngWriter::Write(const std::unique_ptr<Image>& 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;jdx<image->GetHeight();jdx++)
|
||||
{
|
||||
row_pointers[jdx]=(png_byte*)malloc(sizeof(png_byte)*row_size);
|
||||
for(unsigned idx=0;idx<row_size;idx++)
|
||||
{
|
||||
row_pointers[jdx][idx] = image->GetByte(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; y<image->GetHeight(); y++)
|
||||
{
|
||||
free(row_pointers[y]);
|
||||
}
|
||||
free(row_pointers);
|
||||
|
||||
fclose(fp);
|
||||
return;
|
||||
}
|
22
src/image/PngWriter.h
Normal file
22
src/image/PngWriter.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class Image;
|
||||
|
||||
class PngWriter
|
||||
{
|
||||
public:
|
||||
static std::unique_ptr<PngWriter> Create();
|
||||
|
||||
void SetPath(const std::string& path);
|
||||
|
||||
void Write(const std::unique_ptr<Image>& image) const;
|
||||
|
||||
private:
|
||||
|
||||
std::string mPath;
|
||||
};
|
||||
|
||||
using PngWriterPtr = std::unique_ptr<PngWriter>;
|
|
@ -8,9 +8,6 @@
|
|||
|
||||
class NetworkManager
|
||||
{
|
||||
std::vector<SocketPtr> mActiveSockets;
|
||||
ISocketInterfaceUPtr mSocketInterface;
|
||||
|
||||
public:
|
||||
|
||||
NetworkManager();
|
||||
|
@ -24,6 +21,10 @@ public:
|
|||
void RunHttpServer();
|
||||
|
||||
void ShutDown();
|
||||
|
||||
private:
|
||||
std::vector<SocketPtr> mActiveSockets;
|
||||
ISocketInterfaceUPtr mSocketInterface;
|
||||
};
|
||||
|
||||
using NetworkManagerUPtr = std::unique_ptr<NetworkManager>;
|
||||
|
|
|
@ -29,7 +29,7 @@ public:
|
|||
private:
|
||||
|
||||
SocketHandle mHandle;
|
||||
unsigned mPort;
|
||||
unsigned mPort{0};
|
||||
std::string mMessage;
|
||||
};
|
||||
|
||||
|
|
36
src/publishing/CMakeLists.txt
Normal file
36
src/publishing/CMakeLists.txt
Normal file
|
@ -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)
|
45
src/publishing/pdf/PdfDictionary.cpp
Normal file
45
src/publishing/pdf/PdfDictionary.cpp
Normal file
|
@ -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<PdfDictionary*>(GetDict(key));
|
||||
if (first)
|
||||
{
|
||||
content += " /" + key + " " + pdfDict->ToString() + ending;
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
content += " /" + key + " " + pdfDict->ToString() + ending;
|
||||
}
|
||||
}
|
||||
|
||||
content += " >>\n";
|
||||
return content;
|
||||
}
|
10
src/publishing/pdf/PdfDictionary.h
Normal file
10
src/publishing/pdf/PdfDictionary.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include "Dictionary.h"
|
||||
|
||||
class PdfDictionary : public Dictionary
|
||||
{
|
||||
public:
|
||||
|
||||
std::string ToString() const;
|
||||
};
|
73
src/publishing/pdf/PdfDocument.cpp
Normal file
73
src/publishing/pdf/PdfDocument.cpp
Normal file
|
@ -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<PdfXRefTable>();
|
||||
mCatalog = std::make_unique<PdfDocumentCatalog>();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
44
src/publishing/pdf/PdfDocument.h
Normal file
44
src/publishing/pdf/PdfDocument.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
#pragma once
|
||||
|
||||
#include "PdfDictionary.h"
|
||||
#include "StringUtils.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class PdfDocumentCatalog;
|
||||
using PdfDocumentCatalogPtr = std::unique_ptr<PdfDocumentCatalog>;
|
||||
|
||||
class PdfXRefTable;
|
||||
using PdfXRefTablePtr = std::unique_ptr<PdfXRefTable>;
|
||||
|
||||
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<PdfDocument>;
|
44
src/publishing/pdf/PdfDocumentCatalog.cpp
Normal file
44
src/publishing/pdf/PdfDocumentCatalog.cpp
Normal file
|
@ -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<PdfOutlineCollection>();
|
||||
mPages = std::make_unique<PdfPageTree>();
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
23
src/publishing/pdf/PdfDocumentCatalog.h
Normal file
23
src/publishing/pdf/PdfDocumentCatalog.h
Normal file
|
@ -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<PdfOutlineCollection> mOutlines;
|
||||
std::unique_ptr<PdfPageTree> mPages;
|
||||
};
|
58
src/publishing/pdf/PdfObject.cpp
Normal file
58
src/publishing/pdf/PdfObject.cpp
Normal file
|
@ -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;
|
||||
}
|
40
src/publishing/pdf/PdfObject.h
Normal file
40
src/publishing/pdf/PdfObject.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
|
||||
#include "PdfDictionary.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
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<PdfObject>;
|
40
src/publishing/pdf/PdfOutline.cpp
Normal file
40
src/publishing/pdf/PdfOutline.cpp
Normal file
|
@ -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()));
|
||||
}
|
26
src/publishing/pdf/PdfOutline.h
Normal file
26
src/publishing/pdf/PdfOutline.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include "PdfObject.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class PdfOutline : public PdfObject
|
||||
{
|
||||
public:
|
||||
std::string ToString(PdfXRefTable* xRefTable) override;
|
||||
};
|
||||
using PdfOutlinePtr = std::unique_ptr<PdfOutline>;
|
||||
|
||||
class PdfOutlineCollection : public PdfObject
|
||||
{
|
||||
public:
|
||||
|
||||
unsigned IndexObjects(unsigned count) override;
|
||||
|
||||
std::string ToString(PdfXRefTable* xRefTable) override;
|
||||
|
||||
void UpdateDictionary();
|
||||
|
||||
private:
|
||||
std::vector<PdfOutlinePtr> mOutlines;
|
||||
};
|
0
src/publishing/pdf/PdfPage.cpp
Normal file
0
src/publishing/pdf/PdfPage.cpp
Normal file
121
src/publishing/pdf/PdfPage.h
Normal file
121
src/publishing/pdf/PdfPage.h
Normal file
|
@ -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<PdfStream>();
|
||||
|
||||
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<PdfProcSet>();
|
||||
|
||||
mDefaultFont = std::make_unique<PdfFont>();
|
||||
}
|
||||
|
||||
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<PdfDictionary>();
|
||||
resourcesDict->AddStringItem("ProcSet", mProcSet->GetRefString());
|
||||
|
||||
auto fontDict = std::make_unique<PdfDictionary>();
|
||||
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<PdfStream> mContent;
|
||||
std::unique_ptr<PdfFont> mDefaultFont;
|
||||
PdfObjectPtr mProcSet;
|
||||
PdfPageTree* mParent;
|
||||
|
||||
};
|
37
src/publishing/pdf/PdfPageTree.cpp
Normal file
37
src/publishing/pdf/PdfPageTree.cpp
Normal file
|
@ -0,0 +1,37 @@
|
|||
#include "PdfPageTree.h"
|
||||
|
||||
#include "PdfXRefTable.h"
|
||||
#include "PdfPage.h"
|
||||
|
||||
PdfPageTree::PdfPageTree()
|
||||
{
|
||||
mRootPage = std::make_unique<PdfPage>(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");
|
||||
}
|
23
src/publishing/pdf/PdfPageTree.h
Normal file
23
src/publishing/pdf/PdfPageTree.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#include "PdfObject.h"
|
||||
|
||||
class PdfPage;
|
||||
using PdfPagePtr = std::unique_ptr<PdfPage>;
|
||||
|
||||
class PdfPageTree : public PdfObject
|
||||
{
|
||||
public:
|
||||
|
||||
PdfPageTree();
|
||||
|
||||
unsigned IndexObjects(unsigned count) override;
|
||||
|
||||
std::string ToString(PdfXRefTable* xRefTable) override;
|
||||
|
||||
void UpdateDictionary() override;
|
||||
|
||||
private:
|
||||
|
||||
PdfPagePtr mRootPage;
|
||||
};
|
36
src/publishing/pdf/PdfStream.cpp
Normal file
36
src/publishing/pdf/PdfStream.cpp
Normal file
|
@ -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;
|
||||
}
|
19
src/publishing/pdf/PdfStream.h
Normal file
19
src/publishing/pdf/PdfStream.h
Normal file
|
@ -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;
|
||||
};
|
12
src/publishing/pdf/PdfWriter.cpp
Normal file
12
src/publishing/pdf/PdfWriter.cpp
Normal file
|
@ -0,0 +1,12 @@
|
|||
#include "PdfWriter.h"
|
||||
|
||||
#include "PdfDocument.h"
|
||||
|
||||
std::string PdfWriter::ToString(const std::unique_ptr<PdfDocument>& document) const
|
||||
{
|
||||
std::string content;
|
||||
|
||||
content += document->ToString();
|
||||
|
||||
return content;
|
||||
}
|
13
src/publishing/pdf/PdfWriter.h
Normal file
13
src/publishing/pdf/PdfWriter.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class PdfDocument;
|
||||
|
||||
class PdfWriter
|
||||
{
|
||||
public:
|
||||
|
||||
std::string ToString(const std::unique_ptr<PdfDocument>& document) const;
|
||||
};
|
68
src/publishing/pdf/PdfXRefTable.cpp
Normal file
68
src/publishing/pdf/PdfXRefTable.cpp
Normal file
|
@ -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;
|
||||
}
|
||||
|
37
src/publishing/pdf/PdfXRefTable.h
Normal file
37
src/publishing/pdf/PdfXRefTable.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
struct XRefRecord
|
||||
{
|
||||
unsigned mOffsetBytes{0};
|
||||
unsigned mGenerationNumber{0};
|
||||
bool mIsFree{false};
|
||||
};
|
||||
|
||||
struct TableSubSection
|
||||
{
|
||||
unsigned mStartIndex{0};
|
||||
std::vector<XRefRecord> 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<TableSubSection> mSections;
|
||||
};
|
30
src/video/CMakeLists.txt
Normal file
30
src/video/CMakeLists.txt
Normal file
|
@ -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)
|
118
src/video/FfmegInterface.cpp
Normal file
118
src/video/FfmegInterface.cpp
Normal file
|
@ -0,0 +1,118 @@
|
|||
#include "FfmpegInterface.h"
|
||||
|
||||
#include "Image.h"
|
||||
#include "Video.h"
|
||||
#include <iostream>
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavutil/dict.h>
|
||||
#include <libavutil/opt.h>
|
||||
#include <libavutil/imgutils.h>
|
||||
#include <libswscale/swscale.h>
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<Image> > FfmpegInterface::decodeToImages(const std::unique_ptr<Video>& video, unsigned maxImages)
|
||||
{
|
||||
std::vector<std::unique_ptr<Image> > images;
|
||||
|
||||
AVFormatContext* fmt_ctx{nullptr};
|
||||
|
||||
auto ret = avformat_open_input(&fmt_ctx, video->GetPath().c_str(), nullptr, nullptr);
|
||||
if (ret)
|
||||
{
|
||||
return images;
|
||||
}
|
||||
|
||||
//AVDictionaryEntry* tag{nullptr};
|
||||
//while ((tag = av_dict_get(fmt_ctx->metadata, "", tag, AV_DICT_IGNORE_SUFFIX)))
|
||||
//{
|
||||
// std::cout << "key: " << tag->key << " value: " << tag->value << std::endl;
|
||||
//}
|
||||
|
||||
ret = avformat_find_stream_info(fmt_ctx, nullptr);
|
||||
|
||||
AVCodec* decoder;
|
||||
const auto bestStreamIdx = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0);
|
||||
|
||||
//std::cout << "Best stream idx = " << std::to_string(bestStreamIdx) << std::endl;
|
||||
//std::cout << "Codec is: " << decoder->name << " |" << decoder->long_name << std::endl;
|
||||
|
||||
AVCodecContext* decoder_context = avcodec_alloc_context3(decoder);
|
||||
|
||||
avcodec_parameters_to_context(decoder_context, fmt_ctx->streams[bestStreamIdx]->codecpar);
|
||||
av_opt_set_int(decoder_context, "refcounted_frames", 1, 0);
|
||||
|
||||
avcodec_open2(decoder_context, decoder, nullptr);
|
||||
|
||||
AVPacket packet;
|
||||
AVFrame* frame = av_frame_alloc();
|
||||
AVFrame* scaled_frame = av_frame_alloc();
|
||||
|
||||
int w = decoder_context->width;
|
||||
int h = decoder_context->height;
|
||||
|
||||
scaled_frame->width = w;
|
||||
scaled_frame->height = h;
|
||||
scaled_frame->format = AV_PIX_FMT_RGB24;
|
||||
|
||||
av_image_alloc(scaled_frame->data, scaled_frame->linesize, w, h, AV_PIX_FMT_RGB24, 32);
|
||||
|
||||
|
||||
auto img_convert_ctx = sws_getContext(w, h,
|
||||
decoder_context->pix_fmt,
|
||||
w, h, AV_PIX_FMT_RGB24, SWS_BICUBIC,
|
||||
NULL, NULL, NULL);
|
||||
|
||||
|
||||
for (unsigned idx=0; idx<100; idx++)
|
||||
{
|
||||
auto frame_ret = av_read_frame(fmt_ctx, &packet);
|
||||
if (frame_ret)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (packet.stream_index == bestStreamIdx)
|
||||
{
|
||||
avcodec_send_packet(decoder_context, &packet);
|
||||
ret = 0;
|
||||
while(ret >= 0)
|
||||
{
|
||||
ret = avcodec_receive_frame(decoder_context, frame);
|
||||
|
||||
if (ret >= 0)
|
||||
{
|
||||
frame->pts = frame->best_effort_timestamp;
|
||||
|
||||
sws_scale(img_convert_ctx, frame->data,
|
||||
frame->linesize, 0,
|
||||
decoder_context->height,
|
||||
scaled_frame->data, scaled_frame->linesize);
|
||||
|
||||
auto image = Image::Create(frame->width, frame->height);
|
||||
image->SetNumChannels(3);
|
||||
|
||||
auto bufferSize = av_image_get_buffer_size(AV_PIX_FMT_RGB24, frame->width, frame->height, 32);
|
||||
std::vector<unsigned char> data(bufferSize, 0);
|
||||
|
||||
auto copyRet = av_image_copy_to_buffer(data.data(), bufferSize,
|
||||
scaled_frame->data, scaled_frame->linesize,
|
||||
AV_PIX_FMT_RGB24, frame->width, frame->height, 32);
|
||||
|
||||
image->SetData(data);
|
||||
images.push_back(std::move(image));
|
||||
}
|
||||
av_frame_unref(frame);
|
||||
//av_frame_unref(scaled_frame);
|
||||
}
|
||||
}
|
||||
av_packet_unref(&packet);
|
||||
}
|
||||
|
||||
avcodec_free_context(&decoder_context);
|
||||
avformat_close_input(&fmt_ctx);
|
||||
av_frame_free(&frame);
|
||||
|
||||
return images;
|
||||
}
|
15
src/video/FfmpegInterface.h
Normal file
15
src/video/FfmpegInterface.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
class Image;
|
||||
class Video;
|
||||
|
||||
class FfmpegInterface
|
||||
{
|
||||
public:
|
||||
|
||||
std::vector<std::unique_ptr<Image> > decodeToImages(const std::unique_ptr<Video>& video, unsigned maxImages=0);
|
||||
|
||||
};
|
16
src/video/Video.cpp
Normal file
16
src/video/Video.cpp
Normal file
|
@ -0,0 +1,16 @@
|
|||
#include "Video.h"
|
||||
|
||||
std::unique_ptr<Video> Video::Create()
|
||||
{
|
||||
return std::make_unique<Video>();
|
||||
}
|
||||
|
||||
void Video::SetPath(const std::string& path)
|
||||
{
|
||||
mPath = path;
|
||||
}
|
||||
|
||||
std::string Video::GetPath() const
|
||||
{
|
||||
return mPath;
|
||||
}
|
18
src/video/Video.h
Normal file
18
src/video/Video.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class Video
|
||||
{
|
||||
public:
|
||||
static std::unique_ptr<Video> Create();
|
||||
void SetPath(const std::string& path);
|
||||
std::string GetPath() const;
|
||||
|
||||
private:
|
||||
|
||||
std::string mPath;
|
||||
};
|
||||
|
||||
using VideoPtr = std::unique_ptr<Video>;
|
|
@ -8,6 +8,9 @@ list(APPEND web_LIB_INCLUDES
|
|||
markdown/MarkdownParser.cpp
|
||||
html/HtmlWriter.cpp
|
||||
html/HtmlDocument.cpp
|
||||
html/HtmlElement.cpp
|
||||
html/elements/HtmlHeadElement.cpp
|
||||
html/elements/HtmlBodyElement.cpp
|
||||
DocumentConverter.cpp)
|
||||
|
||||
# add the executable
|
||||
|
@ -21,6 +24,7 @@ target_include_directories(web PUBLIC
|
|||
"${CMAKE_CURRENT_SOURCE_DIR}/xml"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/xml/xml-elements"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/html"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/html/elements"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/markdown"
|
||||
)
|
||||
set_property(TARGET web PROPERTY FOLDER src)
|
||||
|
|
|
@ -1,9 +1,19 @@
|
|||
#include "HtmlDocument.h"
|
||||
|
||||
HtmlDocument::HtmlDocument()
|
||||
: XmlDocument()
|
||||
{
|
||||
#include "HtmlHeadElement.h"
|
||||
#include "HtmlBodyElement.h"
|
||||
|
||||
HtmlDocument::HtmlDocument()
|
||||
: XmlDocument()
|
||||
{
|
||||
auto root = XmlElement::Create("html");
|
||||
SetRoot(std::move(root));
|
||||
|
||||
auto header = std::make_unique<HtmlHeadElement>();
|
||||
GetRoot()->AddChild(std::move(header));
|
||||
|
||||
auto body = std::make_unique<HtmlBodyElement>();
|
||||
GetRoot()->AddChild(std::move(body));
|
||||
}
|
||||
|
||||
std::shared_ptr<HtmlDocument> HtmlDocument::Create()
|
||||
|
|
|
@ -6,9 +6,10 @@ class HtmlDocument : public XmlDocument
|
|||
{
|
||||
public:
|
||||
|
||||
HtmlDocument();
|
||||
HtmlDocument();
|
||||
|
||||
static std::shared_ptr<HtmlDocument> Create();
|
||||
|
||||
static std::shared_ptr<HtmlDocument> Create();
|
||||
};
|
||||
|
||||
using HtmlDocumentPtr = std::shared_ptr<HtmlDocument>;
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include "HtmlDocument.h";
|
||||
|
||||
class HtmlDomManager
|
||||
{
|
||||
public:
|
||||
|
||||
HtmlDomManager();
|
||||
|
||||
HtmlDocument* GetDocument();
|
||||
|
||||
private:
|
||||
HtmlDocumentPtr mDocument;
|
||||
};
|
|
@ -6,7 +6,7 @@ HtmlElement::HtmlElement(const std::string& tagName)
|
|||
|
||||
}
|
||||
|
||||
static std::unique_ptr<HtmlElement> HtmlElement::CreateUnique(const std::string& tagName)
|
||||
std::unique_ptr<HtmlElement> HtmlElement::CreateUnique(const std::string& tagName)
|
||||
{
|
||||
return std::make_unique<HtmlElement>(tagName);
|
||||
}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
#pragma once
|
||||
#include <memory>
|
||||
|
||||
#include "../xml/xml-elements/XmlElement.h"
|
||||
#include "XmlElement.h"
|
||||
|
||||
class HtmlElement : public XmlElement
|
||||
{
|
||||
HtmlElement(const std::string& tagName);
|
||||
public:
|
||||
HtmlElement(const std::string& tagName);
|
||||
|
||||
static std::unique_ptr<HtmlElement> CreateUnique(const std::string& tagName);
|
||||
static std::unique_ptr<HtmlElement> CreateUnique(const std::string& tagName);
|
||||
};
|
||||
|
||||
using HtmlElementUPtr = std::unique_ptr<HtmlElement>;
|
||||
|
|
|
@ -5,7 +5,58 @@ HtmlWriter::HtmlWriter()
|
|||
|
||||
}
|
||||
|
||||
std::string HtmlWriter::ToString(XmlElement* element, unsigned depth)
|
||||
{
|
||||
const auto prefix = std::string(2*depth, ' ');
|
||||
|
||||
auto content = prefix + "<" + element->GetTagName();
|
||||
for (std::size_t idx=0; idx< element->GetNumAttributes(); idx++)
|
||||
{
|
||||
auto attribute = element->GetAttribute(idx);
|
||||
content += " " + attribute->GetName() + "=\"" + attribute->GetValue() + "\"";
|
||||
}
|
||||
|
||||
const auto num_children = element->GetNumChildren();
|
||||
if (num_children == 0 && element->GetText().empty())
|
||||
{
|
||||
content += "/>\n";
|
||||
return content;
|
||||
}
|
||||
else
|
||||
{
|
||||
content += ">";
|
||||
}
|
||||
|
||||
if (!element->GetText().empty())
|
||||
{
|
||||
content += element->GetText();
|
||||
}
|
||||
|
||||
if (num_children>0)
|
||||
{
|
||||
content += "\n";
|
||||
}
|
||||
for (std::size_t idx=0; idx< element->GetNumChildren(); idx++)
|
||||
{
|
||||
auto child = element->GetChild(idx);
|
||||
content += ToString(child, depth+1);
|
||||
}
|
||||
if (num_children>0)
|
||||
{
|
||||
content += prefix;
|
||||
}
|
||||
|
||||
content += "</" + element->GetTagName() + ">\n";
|
||||
return content;
|
||||
}
|
||||
|
||||
std::string HtmlWriter::ToString(HtmlDocumentPtr document)
|
||||
{
|
||||
return "<html>Uh oh!!!!</html>";
|
||||
std::string content = "<!DOCTYPE html>\n";
|
||||
|
||||
if (auto root = document->GetRoot())
|
||||
{
|
||||
content += ToString(root);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,10 @@ class HtmlWriter
|
|||
{
|
||||
public:
|
||||
|
||||
HtmlWriter();
|
||||
HtmlWriter();
|
||||
|
||||
std::string ToString(HtmlDocumentPtr document);
|
||||
std::string ToString(HtmlDocumentPtr document);
|
||||
|
||||
private:
|
||||
std::string ToString(XmlElement* element, unsigned depth=0);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include "HtmlElement.h"
|
||||
|
||||
class HtmlBodyElement : public HtmlElement
|
||||
{
|
||||
public:
|
||||
HtmlBodyElement() : HtmlElement("body")
|
||||
{
|
||||
|
||||
}
|
||||
};
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include "HtmlElement.h"
|
||||
|
||||
class HtmlHeadElement : public HtmlElement
|
||||
{
|
||||
public:
|
||||
HtmlHeadElement() : HtmlElement("head")
|
||||
{
|
||||
|
||||
}
|
||||
};
|
|
@ -1,17 +1,30 @@
|
|||
#include "MarkdownParser.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
MarkdownParser::MarkdownParser()
|
||||
: mHtmlDocument(HtmlDocument::Create())
|
||||
: mHtmlDocument(HtmlDocument::Create())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void MarkdownParser::ProcessLine(const std::string)
|
||||
void MarkdownParser::ProcessLine(const std::string& line)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void MarkdownParser::Run(const std::string& content)
|
||||
{
|
||||
std::stringstream ss(content);
|
||||
std::string line;
|
||||
while (std::getline(ss, line, '\n'))
|
||||
{
|
||||
ProcessLine(line);
|
||||
}
|
||||
}
|
||||
|
||||
HtmlDocumentPtr MarkdownParser::GetHtml()
|
||||
{
|
||||
return mHtmlDocument;
|
||||
return mHtmlDocument;
|
||||
}
|
||||
|
|
|
@ -4,13 +4,27 @@
|
|||
|
||||
class MarkdownParser
|
||||
{
|
||||
HtmlDocumentPtr mHtmlDocument;
|
||||
enum class DocumentState
|
||||
{
|
||||
None
|
||||
};
|
||||
|
||||
enum class LineState
|
||||
{
|
||||
None
|
||||
};
|
||||
|
||||
|
||||
DocumentState mDocumentState {DocumentState::None};
|
||||
HtmlDocumentPtr mHtmlDocument;
|
||||
|
||||
public:
|
||||
|
||||
MarkdownParser();
|
||||
MarkdownParser();
|
||||
|
||||
void ProcessLine(const std::string);
|
||||
HtmlDocumentPtr GetHtml();
|
||||
|
||||
HtmlDocumentPtr GetHtml();
|
||||
void ProcessLine(const std::string& line);
|
||||
|
||||
void Run(const std::string& content);
|
||||
};
|
||||
|
|
|
@ -9,15 +9,14 @@ XmlParser::XmlParser()
|
|||
: mDocument(XmlDocument::Create()),
|
||||
mDocumentState(XmlParser::DocumentState::Await_Prolog),
|
||||
mLineState(XmlParser::LineState::Await_Tag_Open),
|
||||
mParentElement(nullptr),
|
||||
mWorkingElement(nullptr)
|
||||
mWorkingElements()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void XmlParser::ProcessLine(const std::string& input)
|
||||
{
|
||||
for(std::size_t idx=0; idx<input.size(); idx++)
|
||||
for (std::size_t idx=0; idx<input.size(); idx++)
|
||||
{
|
||||
switch (input[idx])
|
||||
{
|
||||
|
@ -269,14 +268,11 @@ void XmlParser::OnTagClose()
|
|||
}
|
||||
else if(mDocumentState == DS::Close_Element)
|
||||
{
|
||||
if(mWorkingElement->GetTagName() == mWorkingTagName)
|
||||
mDocumentState = DS::Await_Element;
|
||||
mLineState = LS::Await_Tag_Open;
|
||||
if (!mWorkingElements.empty())
|
||||
{
|
||||
mDocumentState = DS::Await_Element;
|
||||
mLineState = LS::Await_Tag_Open;
|
||||
if(mParentElement)
|
||||
{
|
||||
mWorkingElement = mParentElement;
|
||||
}
|
||||
mWorkingElements.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -289,7 +285,7 @@ void XmlParser::OnTextStart(char c)
|
|||
|
||||
void XmlParser::OnTextEnd()
|
||||
{
|
||||
mWorkingElement->SetText(mWorkingText);
|
||||
mWorkingElements.top()->SetText(mWorkingText);
|
||||
}
|
||||
|
||||
void XmlParser::onElementTagEnd()
|
||||
|
@ -318,14 +314,17 @@ void XmlParser::OnTagNameEnd()
|
|||
else if(mDocumentState == DS::Build_Element)
|
||||
{
|
||||
auto new_element = XmlElement::Create(mWorkingTagName);
|
||||
auto working_element = new_element.get();
|
||||
|
||||
mParentElement = mWorkingElement;
|
||||
mWorkingElement = new_element.get();
|
||||
|
||||
if(!mDocument->GetRoot())
|
||||
if (!mDocument->GetRoot())
|
||||
{
|
||||
mDocument->SetRoot(std::move(new_element));
|
||||
}
|
||||
else
|
||||
{
|
||||
mWorkingElements.top()->AddChild(std::move(new_element));
|
||||
}
|
||||
mWorkingElements.push(working_element);
|
||||
mLineState = LS::Await_Attribute_Name;
|
||||
}
|
||||
}
|
||||
|
@ -345,7 +344,7 @@ void XmlParser::OnAttributeNameEnd()
|
|||
}
|
||||
else if(mDocumentState == DS::Build_Element)
|
||||
{
|
||||
mWorkingElement->AddAttribute(std::move(attribute));
|
||||
mWorkingElements.top()->AddAttribute(std::move(attribute));
|
||||
}
|
||||
mLineState = LS::Await_Attribute_Value;
|
||||
}
|
||||
|
@ -358,7 +357,14 @@ void XmlParser::OnAttributeValueStart()
|
|||
|
||||
void XmlParser::OnAttributeValueEnd()
|
||||
{
|
||||
mWorkingElement->GetAttribute(mWorkingAttributeName)->SetValue(mWorkingAttributeValue);
|
||||
if(mDocumentState == DS::Build_Prolog)
|
||||
{
|
||||
mDocument->GetProlog()->GetAttribute(mWorkingAttributeName)->SetValue(mWorkingAttributeValue);
|
||||
}
|
||||
else if(mDocumentState == DS::Build_Element)
|
||||
{
|
||||
mWorkingElements.top()->GetAttribute(mWorkingAttributeName)->SetValue(mWorkingAttributeValue);
|
||||
}
|
||||
mLineState = LS::Await_Attribute_Name;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <stack>
|
||||
#include "XmlDocument.h"
|
||||
|
||||
class XmlParser
|
||||
|
@ -33,8 +34,7 @@ private:
|
|||
DocumentState mDocumentState;
|
||||
LineState mLineState;
|
||||
XmlDocumentPtr mDocument;
|
||||
XmlElement* mParentElement;
|
||||
XmlElement* mWorkingElement;
|
||||
std::stack<XmlElement*> mWorkingElements;
|
||||
std::string mWorkingAttributeName;
|
||||
std::string mWorkingTagName;
|
||||
std::string mWorkingAttributeValue;
|
||||
|
|
|
@ -1,6 +1,68 @@
|
|||
#include "XmlWriter.h"
|
||||
|
||||
void XmlWriter::Write(File* file, XmlDocument* document)
|
||||
{
|
||||
|
||||
std::string XmlWriter::ToString(XmlElement* element, unsigned depth)
|
||||
{
|
||||
const auto prefix = std::string(2*depth, ' ');
|
||||
|
||||
auto content = prefix + "<" + element->GetTagName();
|
||||
for (std::size_t idx=0; idx< element->GetNumAttributes(); idx++)
|
||||
{
|
||||
auto attribute = element->GetAttribute(idx);
|
||||
content += " " + attribute->GetName() + "=\"" + attribute->GetValue() + "\"";
|
||||
}
|
||||
|
||||
const auto num_children = element->GetNumChildren();
|
||||
if (num_children == 0 && element->GetText().empty())
|
||||
{
|
||||
content += "/>\n";
|
||||
return content;
|
||||
}
|
||||
else
|
||||
{
|
||||
content += ">";
|
||||
}
|
||||
|
||||
if (!element->GetText().empty())
|
||||
{
|
||||
content += element->GetText();
|
||||
}
|
||||
|
||||
if (num_children>0)
|
||||
{
|
||||
content += "\n";
|
||||
}
|
||||
for (std::size_t idx=0; idx< element->GetNumChildren(); idx++)
|
||||
{
|
||||
auto child = element->GetChild(idx);
|
||||
content += ToString(child, depth+1);
|
||||
}
|
||||
if (num_children>0)
|
||||
{
|
||||
content += prefix;
|
||||
}
|
||||
|
||||
content += "</" + element->GetTagName() + ">\n";
|
||||
return content;
|
||||
}
|
||||
|
||||
std::string XmlWriter::ToString(XmlDocument* document)
|
||||
{
|
||||
std::string content;
|
||||
if (auto prolog = document->GetProlog())
|
||||
{
|
||||
content += "<?xml";
|
||||
for (std::size_t idx=0; idx< prolog->GetNumAttributes(); idx++)
|
||||
{
|
||||
auto attribute = prolog->GetAttribute(idx);
|
||||
content += " " + attribute->GetName() + "=\"" + attribute->GetValue() + "\"";
|
||||
}
|
||||
content += "?>\n";
|
||||
}
|
||||
|
||||
if (auto root = document->GetRoot())
|
||||
{
|
||||
content += ToString(root);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include "File.h"
|
||||
#include "XmlDocument.h"
|
||||
#include <string>
|
||||
|
||||
class XmlWriter
|
||||
{
|
||||
public:
|
||||
XmlWriter() = default;
|
||||
|
||||
void Write(File* file, XmlDocument* document);
|
||||
std::string ToString(XmlDocument* document);
|
||||
|
||||
private:
|
||||
std::string ToString(XmlElement* element, unsigned depth=0);
|
||||
};
|
||||
|
|
|
@ -68,3 +68,13 @@ std::size_t XmlElement::GetNumAttributes() const
|
|||
{
|
||||
return mAttributes.size();
|
||||
}
|
||||
|
||||
std::size_t XmlElement::GetNumChildren() const
|
||||
{
|
||||
return mChildren.size();
|
||||
}
|
||||
|
||||
XmlElement* XmlElement::GetChild(std::size_t index) const
|
||||
{
|
||||
return mChildren[index].get();
|
||||
}
|
||||
|
|
|
@ -1,18 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "XmlAttribute.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "xml-elements/XmlAttribute.h"
|
||||
|
||||
class XmlElement
|
||||
{
|
||||
protected:
|
||||
std::string mTagName;
|
||||
std::vector<XmlAttributeUPtr> mAttributes;
|
||||
std::vector<std::unique_ptr<XmlElement> > mChildren;
|
||||
std::string mText;
|
||||
|
||||
public:
|
||||
XmlElement(const std::string& tagName);
|
||||
virtual ~XmlElement() = default;
|
||||
|
@ -28,8 +23,17 @@ public:
|
|||
XmlAttribute* GetAttribute(std::size_t index) const;
|
||||
std::size_t GetNumAttributes() const;
|
||||
|
||||
std::size_t GetNumChildren() const;
|
||||
XmlElement* GetChild(std::size_t index) const;
|
||||
|
||||
void SetText(const std::string& text);
|
||||
void SetTagName(const std::string& tagName);
|
||||
|
||||
protected:
|
||||
std::string mTagName;
|
||||
std::vector<XmlAttributeUPtr> mAttributes;
|
||||
std::vector<std::unique_ptr<XmlElement> > mChildren;
|
||||
std::string mText;
|
||||
};
|
||||
|
||||
using XmlElementPtr = std::shared_ptr<XmlElement>;
|
||||
|
|
|
@ -2,12 +2,11 @@
|
|||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "xml-elements/XmlElement.h"
|
||||
#include "XmlElement.h"
|
||||
|
||||
class XmlProlog : public XmlElement
|
||||
{
|
||||
public:
|
||||
|
||||
enum class Version{
|
||||
V1_0
|
||||
};
|
||||
|
|
|
@ -10,16 +10,35 @@ target_include_directories(test_utils PUBLIC
|
|||
list(APPEND TestFiles
|
||||
audio/TestAudioWriter.cpp
|
||||
audio/TestMidiReader.cpp
|
||||
graphics/TestOpenGlRendering.cpp)
|
||||
database/TestDatabase.cpp
|
||||
graphics/TestOpenGlRendering.cpp
|
||||
ipc/TestDbus.cpp
|
||||
image/TestPngWriter.cpp
|
||||
publishing/TestPdfWriter.cpp
|
||||
video/TestVideoDecoder.cpp
|
||||
web/TestMarkdownParser.cpp
|
||||
web/TestXmlParser.cpp)
|
||||
|
||||
list(APPEND TestNames
|
||||
TestAudioWriter
|
||||
TestMidiReader
|
||||
TestOpenGlRendering)
|
||||
TestDatabase
|
||||
TestOpenGlRendering
|
||||
TestDbus
|
||||
TestPngWriter
|
||||
TestPdfWriter
|
||||
TestVideoDecoder
|
||||
TestMarkdownParser
|
||||
TestXmlParser)
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(DBUS REQUIRED dbus-1)
|
||||
include_directories(${DBUS_INCLUDE_DIRS})
|
||||
link_directories(${DBUS_LIBRARY_DIRS})
|
||||
|
||||
foreach(TestFile TestName IN ZIP_LISTS TestFiles TestNames)
|
||||
add_executable(${TestName} ${TestFile})
|
||||
target_link_libraries(${TestName} PUBLIC core network database geometry audio graphics web client test_utils)
|
||||
target_link_libraries(${TestName} PUBLIC core network image publishing video database geometry audio graphics web client test_utils ${DBUS_LIBRARIES})
|
||||
endforeach()
|
||||
|
||||
|
||||
|
|
|
@ -8,7 +8,9 @@
|
|||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#include <iostream>
|
||||
|
||||
class TestWriteWav : public TestCase
|
||||
|
@ -32,9 +34,11 @@ class TestAudioRender : public TestCase
|
|||
public:
|
||||
bool Run() override
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WasapiInterface audio_interface;
|
||||
auto device = AudioDevice::Create();
|
||||
audio_interface.Play(device);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
@ -42,8 +46,9 @@ public:
|
|||
//int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
|
||||
int main()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
|
||||
|
||||
#endif
|
||||
std::cout << "into entry point" << std::endl;
|
||||
TestCaseRunner runner;
|
||||
//runner.AddTestCase("TestWriteNav", std::make_unique<TestWriteWav>());
|
||||
|
@ -51,6 +56,7 @@ int main()
|
|||
|
||||
const auto testsPassed = runner.Run();
|
||||
return testsPassed ? 0 : -1;
|
||||
|
||||
#ifdef _WIN32
|
||||
CoUninitialize();
|
||||
#endif
|
||||
}
|
||||
|
|
20
test/data/sample_markdown.md
Normal file
20
test/data/sample_markdown.md
Normal file
|
@ -0,0 +1,20 @@
|
|||
# I'm a level one header
|
||||
I'm some text under level one
|
||||
|
||||
## I'm a level two header
|
||||
I'm some text under level two
|
||||
|
||||
```
|
||||
I'm a code block
|
||||
```
|
||||
|
||||
I'm a line under the code block, with some `inline code`.
|
||||
|
||||
### I'm a level three header
|
||||
I'm a bullet point list:
|
||||
|
||||
* First point
|
||||
* Second point
|
||||
* Third point
|
||||
|
||||
With a [hyperlink](www.imahyperlink.com) embedded.
|
12
test/database/TestDatabase.cpp
Normal file
12
test/database/TestDatabase.cpp
Normal file
|
@ -0,0 +1,12 @@
|
|||
#include "DatabaseManager.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
DatabaseManager db_manager;
|
||||
db_manager.CreateDatabase("test.db");
|
||||
|
||||
std::string statement = "CREATE TABLE corporation;";
|
||||
db_manager.Run(statement);
|
||||
|
||||
return 0;
|
||||
}
|
31
test/image/TestPngWriter.cpp
Normal file
31
test/image/TestPngWriter.cpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
#include "Image.h"
|
||||
#include "PngWriter.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
unsigned width = 200;
|
||||
unsigned height = 200;
|
||||
unsigned numChannels = 3;
|
||||
auto image = Image::Create(width, height);
|
||||
image->SetNumChannels(numChannels);
|
||||
|
||||
std::vector<unsigned char> data(image->GetBytesPerRow()*height, 0);
|
||||
for(unsigned jdx=0;jdx<height;jdx++)
|
||||
{
|
||||
const auto heightOffset = jdx*image->GetBytesPerRow();
|
||||
for(unsigned idx=0;idx<width*numChannels;idx+=numChannels)
|
||||
{
|
||||
const auto index = heightOffset + idx;
|
||||
data[index] = (idx%2 == 0) ? 255*jdx/(height+1) : 0;
|
||||
data[index+1] = 0;
|
||||
data[index+2] = 0;
|
||||
}
|
||||
}
|
||||
image->SetData(data);
|
||||
|
||||
PngWriter writer;
|
||||
writer.SetPath("test.png");
|
||||
writer.Write(image);
|
||||
|
||||
return 0;
|
||||
}
|
8
test/ipc/TestDbus.cpp
Normal file
8
test/ipc/TestDbus.cpp
Normal file
|
@ -0,0 +1,8 @@
|
|||
#include <dbus/dbus.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
DBusError err;
|
||||
DBusConnection* conn;
|
||||
return 0;
|
||||
}
|
18
test/publishing/TestPdfWriter.cpp
Normal file
18
test/publishing/TestPdfWriter.cpp
Normal file
|
@ -0,0 +1,18 @@
|
|||
#include "PdfDocument.h"
|
||||
#include "PdfWriter.h"
|
||||
#include "PdfObject.h"
|
||||
#include "File.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
auto document = std::make_unique<PdfDocument>();
|
||||
|
||||
PdfWriter writer;
|
||||
const auto output = writer.ToString(document);
|
||||
|
||||
File pdf_file("TestPdfWriter.pdf");
|
||||
pdf_file.SetAccessMode(File::AccessMode::Write);
|
||||
pdf_file.Open();
|
||||
pdf_file.WriteText(output);
|
||||
return 0;
|
||||
}
|
25
test/video/TestVideoDecoder.cpp
Normal file
25
test/video/TestVideoDecoder.cpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
#include "Video.h"
|
||||
#include "Image.h"
|
||||
#include "FfmpegInterface.h"
|
||||
#include "PngWriter.h"
|
||||
#include <iostream>
|
||||
|
||||
int main()
|
||||
{
|
||||
auto video = Video::Create();
|
||||
video->SetPath("/home/jmsgrogan/test.mp4");
|
||||
|
||||
FfmpegInterface decoder;
|
||||
auto images = decoder.decodeToImages(video, 4);
|
||||
std::cout << "Got " << std::to_string(images.size()) << std::endl;
|
||||
|
||||
PngWriter writer;
|
||||
for(unsigned idx=0; idx<20; idx++)
|
||||
{
|
||||
auto filename = "test_out" + std::to_string(idx) + ".png";
|
||||
writer.SetPath(filename);
|
||||
writer.Write(images[idx]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
25
test/web/TestMarkdownParser.cpp
Normal file
25
test/web/TestMarkdownParser.cpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
#include "MarkdownParser.h"
|
||||
#include "File.h"
|
||||
#include "HtmlWriter.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
File md_file("/home/jmsgrogan/code/MediaTool/test/data/sample_markdown.md");
|
||||
md_file.Open();
|
||||
const auto md_content = md_file.ReadText();
|
||||
|
||||
MarkdownParser parser;
|
||||
parser.Run(md_content);
|
||||
|
||||
auto html = parser.GetHtml();
|
||||
|
||||
HtmlWriter writer;
|
||||
const auto html_string = writer.ToString(html);
|
||||
|
||||
File html_file("TestMarkdownParserOut.html");
|
||||
html_file.SetAccessMode(File::AccessMode::Write);
|
||||
html_file.Open();
|
||||
html_file.WriteText(html_string);
|
||||
return 0;
|
||||
|
||||
}
|
31
test/web/TestXmlParser.cpp
Normal file
31
test/web/TestXmlParser.cpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include "XmlParser.h"
|
||||
#include "XmlWriter.h"
|
||||
#include "File.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
XmlParser parser;
|
||||
|
||||
std::ifstream xml_file;
|
||||
xml_file.open("/home/jmsgrogan/test.xml", std::ifstream::in);
|
||||
while(xml_file.good())
|
||||
{
|
||||
std::string line;
|
||||
std::getline(xml_file, line);
|
||||
parser.ProcessLine(line);
|
||||
}
|
||||
xml_file.close();
|
||||
|
||||
auto outFile = std::make_unique<File>("test_out.xml");
|
||||
outFile->SetAccessMode(File::AccessMode::Write);
|
||||
outFile->Open();
|
||||
|
||||
XmlWriter writer;
|
||||
auto content = writer.ToString(parser.GetDocument().get());
|
||||
outFile->WriteText(content);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in a new issue