Add PDF writer.

This commit is contained in:
jmsgrogan 2022-01-01 18:46:31 +00:00
parent c05b7b6315
commit 9c116b1efd
72 changed files with 1819 additions and 114 deletions

View file

@ -26,14 +26,5 @@ target_include_directories(sample_console PUBLIC
target_link_libraries(sample_console PUBLIC console core network target_link_libraries(sample_console PUBLIC console core network
database geometry audio web) 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_console PROPERTY FOLDER apps)
set_property(TARGET sample_gui PROPERTY FOLDER apps) set_property(TARGET sample_gui PROPERTY FOLDER apps)
set_property(TARGET xml_practice PROPERTY FOLDER apps)

View file

@ -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;
}

View file

@ -3,9 +3,12 @@ add_subdirectory(database)
add_subdirectory(network) add_subdirectory(network)
add_subdirectory(geometry) add_subdirectory(geometry)
add_subdirectory(audio) add_subdirectory(audio)
add_subdirectory(image)
add_subdirectory(console) add_subdirectory(console)
add_subdirectory(client) add_subdirectory(client)
add_subdirectory(graphics) add_subdirectory(graphics)
add_subdirectory(publishing)
add_subdirectory(video)
add_subdirectory(windows) add_subdirectory(windows)
add_subdirectory(web) add_subdirectory(web)
add_subdirectory(ui_elements) add_subdirectory(ui_elements)

View file

@ -1,5 +1,6 @@
list(APPEND core_HEADERS list(APPEND core_HEADERS
AbstractApp.h AbstractApp.h
Dictionary.h
Event.h Event.h
Color.h Color.h
CommandLineArgs.h CommandLineArgs.h
@ -12,6 +13,7 @@ list(APPEND core_HEADERS
list(APPEND core_LIB_INCLUDES list(APPEND core_LIB_INCLUDES
Event.cpp Event.cpp
Dictionary.cpp
Color.cpp Color.cpp
CommandLineArgs.cpp CommandLineArgs.cpp
loggers/FileLogger.cpp loggers/FileLogger.cpp

56
src/core/Dictionary.cpp Normal file
View 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
View 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;
};

View file

@ -1,6 +1,8 @@
#include "StringUtils.h" #include "StringUtils.h"
#include <locale> #include <locale>
#include <algorithm> #include <algorithm>
#include <sstream>
#include <iomanip>
#ifdef _WIN32 #ifdef _WIN32
#include "Windows.h" #include "Windows.h"
@ -45,3 +47,10 @@ std::string StringUtils::convert(const std::wstring& input)
throw std::logic_error("Not implemented"); throw std::logic_error("Not implemented");
#endif #endif
} }
std::string StringUtils::ToPaddedString(unsigned numBytes, unsigned entry)
{
std::stringstream sstr;
sstr << std::setfill('0') << std::setw(numBytes) << entry;
return sstr.str();
}

View file

@ -18,4 +18,5 @@ public:
static bool IsSpace(char c); static bool IsSpace(char c);
static std::string ToLower(const std::string& s); static std::string ToLower(const std::string& s);
static std::string convert(const std::wstring& input); static std::string convert(const std::wstring& input);
static std::string ToPaddedString(unsigned numBytes, unsigned entry);
}; };

View file

@ -27,6 +27,11 @@ void DatabaseManager::CreateDatabase(const std::string& path)
mDatabaseInterface->Open(mDatabase); mDatabaseInterface->Open(mDatabase);
} }
void DatabaseManager::Run(const std::string& statement)
{
mDatabaseInterface->Run(statement);
}
void DatabaseManager::OnShutDown() void DatabaseManager::OnShutDown()
{ {
if(mDatabaseInterface) if(mDatabaseInterface)

View file

@ -7,10 +7,6 @@
class DatabaseManager class DatabaseManager
{ {
DatabasePtr mDatabase;
SqliteInterfacePtr mDatabaseInterface;
public: public:
DatabaseManager(); DatabaseManager();
@ -21,7 +17,13 @@ public:
void CreateDatabase(const std::string& path); void CreateDatabase(const std::string& path);
void Run(const std::string& statement);
void OnShutDown(); void OnShutDown();
private:
DatabasePtr mDatabase;
SqliteInterfacePtr mDatabaseInterface;
}; };
using DatabaseManagerPtr = std::unique_ptr<DatabaseManager>; using DatabaseManagerPtr = std::unique_ptr<DatabaseManager>;

21
src/image/CMakeLists.txt Normal file
View 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
View 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
View 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
View 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
View 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>;

View file

@ -8,9 +8,6 @@
class NetworkManager class NetworkManager
{ {
std::vector<SocketPtr> mActiveSockets;
ISocketInterfaceUPtr mSocketInterface;
public: public:
NetworkManager(); NetworkManager();
@ -24,6 +21,10 @@ public:
void RunHttpServer(); void RunHttpServer();
void ShutDown(); void ShutDown();
private:
std::vector<SocketPtr> mActiveSockets;
ISocketInterfaceUPtr mSocketInterface;
}; };
using NetworkManagerUPtr = std::unique_ptr<NetworkManager>; using NetworkManagerUPtr = std::unique_ptr<NetworkManager>;

View file

@ -29,7 +29,7 @@ public:
private: private:
SocketHandle mHandle; SocketHandle mHandle;
unsigned mPort; unsigned mPort{0};
std::string mMessage; std::string mMessage;
}; };

View 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)

View 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;
}

View file

@ -0,0 +1,10 @@
#pragma once
#include "Dictionary.h"
class PdfDictionary : public Dictionary
{
public:
std::string ToString() const;
};

View 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);
}

View 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>;

View 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());
}

View 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;
};

View 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;
}

View 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>;

View 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()));
}

View 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;
};

View file

View 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;
};

View 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");
}

View 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;
};

View 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;
}

View 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;
};

View 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;
}

View 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;
};

View 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;
}

View 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
View 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)

View 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;
}

View 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
View 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
View 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>;

View file

@ -8,6 +8,9 @@ list(APPEND web_LIB_INCLUDES
markdown/MarkdownParser.cpp markdown/MarkdownParser.cpp
html/HtmlWriter.cpp html/HtmlWriter.cpp
html/HtmlDocument.cpp html/HtmlDocument.cpp
html/HtmlElement.cpp
html/elements/HtmlHeadElement.cpp
html/elements/HtmlBodyElement.cpp
DocumentConverter.cpp) DocumentConverter.cpp)
# add the executable # add the executable
@ -21,6 +24,7 @@ target_include_directories(web PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}/xml" "${CMAKE_CURRENT_SOURCE_DIR}/xml"
"${CMAKE_CURRENT_SOURCE_DIR}/xml/xml-elements" "${CMAKE_CURRENT_SOURCE_DIR}/xml/xml-elements"
"${CMAKE_CURRENT_SOURCE_DIR}/html" "${CMAKE_CURRENT_SOURCE_DIR}/html"
"${CMAKE_CURRENT_SOURCE_DIR}/html/elements"
"${CMAKE_CURRENT_SOURCE_DIR}/markdown" "${CMAKE_CURRENT_SOURCE_DIR}/markdown"
) )
set_property(TARGET web PROPERTY FOLDER src) set_property(TARGET web PROPERTY FOLDER src)

View file

@ -1,9 +1,19 @@
#include "HtmlDocument.h" #include "HtmlDocument.h"
HtmlDocument::HtmlDocument() #include "HtmlHeadElement.h"
: XmlDocument() #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() std::shared_ptr<HtmlDocument> HtmlDocument::Create()

View file

@ -6,9 +6,10 @@ class HtmlDocument : public XmlDocument
{ {
public: public:
HtmlDocument(); HtmlDocument();
static std::shared_ptr<HtmlDocument> Create();
static std::shared_ptr<HtmlDocument> Create();
}; };
using HtmlDocumentPtr = std::shared_ptr<HtmlDocument>; using HtmlDocumentPtr = std::shared_ptr<HtmlDocument>;

View file

@ -0,0 +1,15 @@
#pragma once
#include "HtmlDocument.h";
class HtmlDomManager
{
public:
HtmlDomManager();
HtmlDocument* GetDocument();
private:
HtmlDocumentPtr mDocument;
};

View file

@ -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); return std::make_unique<HtmlElement>(tagName);
} }

View file

@ -1,13 +1,14 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include "../xml/xml-elements/XmlElement.h" #include "XmlElement.h"
class HtmlElement : public XmlElement 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>; using HtmlElementUPtr = std::unique_ptr<HtmlElement>;

View file

@ -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) 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;
} }

View file

@ -6,7 +6,10 @@ class HtmlWriter
{ {
public: public:
HtmlWriter(); HtmlWriter();
std::string ToString(HtmlDocumentPtr document); std::string ToString(HtmlDocumentPtr document);
private:
std::string ToString(XmlElement* element, unsigned depth=0);
}; };

View file

@ -0,0 +1,12 @@
#pragma once
#include "HtmlElement.h"
class HtmlBodyElement : public HtmlElement
{
public:
HtmlBodyElement() : HtmlElement("body")
{
}
};

View file

@ -0,0 +1,12 @@
#pragma once
#include "HtmlElement.h"
class HtmlHeadElement : public HtmlElement
{
public:
HtmlHeadElement() : HtmlElement("head")
{
}
};

View file

@ -1,17 +1,30 @@
#include "MarkdownParser.h" #include "MarkdownParser.h"
#include <sstream>
#include <iostream>
MarkdownParser::MarkdownParser() 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() HtmlDocumentPtr MarkdownParser::GetHtml()
{ {
return mHtmlDocument; return mHtmlDocument;
} }

View file

@ -4,13 +4,27 @@
class MarkdownParser class MarkdownParser
{ {
HtmlDocumentPtr mHtmlDocument; enum class DocumentState
{
None
};
enum class LineState
{
None
};
DocumentState mDocumentState {DocumentState::None};
HtmlDocumentPtr mHtmlDocument;
public: public:
MarkdownParser(); MarkdownParser();
void ProcessLine(const std::string); HtmlDocumentPtr GetHtml();
HtmlDocumentPtr GetHtml(); void ProcessLine(const std::string& line);
void Run(const std::string& content);
}; };

View file

@ -9,15 +9,14 @@ XmlParser::XmlParser()
: mDocument(XmlDocument::Create()), : mDocument(XmlDocument::Create()),
mDocumentState(XmlParser::DocumentState::Await_Prolog), mDocumentState(XmlParser::DocumentState::Await_Prolog),
mLineState(XmlParser::LineState::Await_Tag_Open), mLineState(XmlParser::LineState::Await_Tag_Open),
mParentElement(nullptr), mWorkingElements()
mWorkingElement(nullptr)
{ {
} }
void XmlParser::ProcessLine(const std::string& input) 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]) switch (input[idx])
{ {
@ -269,14 +268,11 @@ void XmlParser::OnTagClose()
} }
else if(mDocumentState == DS::Close_Element) 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; mWorkingElements.pop();
mLineState = LS::Await_Tag_Open;
if(mParentElement)
{
mWorkingElement = mParentElement;
}
} }
} }
} }
@ -289,7 +285,7 @@ void XmlParser::OnTextStart(char c)
void XmlParser::OnTextEnd() void XmlParser::OnTextEnd()
{ {
mWorkingElement->SetText(mWorkingText); mWorkingElements.top()->SetText(mWorkingText);
} }
void XmlParser::onElementTagEnd() void XmlParser::onElementTagEnd()
@ -318,14 +314,17 @@ void XmlParser::OnTagNameEnd()
else if(mDocumentState == DS::Build_Element) else if(mDocumentState == DS::Build_Element)
{ {
auto new_element = XmlElement::Create(mWorkingTagName); auto new_element = XmlElement::Create(mWorkingTagName);
auto working_element = new_element.get();
mParentElement = mWorkingElement; if (!mDocument->GetRoot())
mWorkingElement = new_element.get();
if(!mDocument->GetRoot())
{ {
mDocument->SetRoot(std::move(new_element)); mDocument->SetRoot(std::move(new_element));
} }
else
{
mWorkingElements.top()->AddChild(std::move(new_element));
}
mWorkingElements.push(working_element);
mLineState = LS::Await_Attribute_Name; mLineState = LS::Await_Attribute_Name;
} }
} }
@ -345,7 +344,7 @@ void XmlParser::OnAttributeNameEnd()
} }
else if(mDocumentState == DS::Build_Element) else if(mDocumentState == DS::Build_Element)
{ {
mWorkingElement->AddAttribute(std::move(attribute)); mWorkingElements.top()->AddAttribute(std::move(attribute));
} }
mLineState = LS::Await_Attribute_Value; mLineState = LS::Await_Attribute_Value;
} }
@ -358,7 +357,14 @@ void XmlParser::OnAttributeValueStart()
void XmlParser::OnAttributeValueEnd() 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; mLineState = LS::Await_Attribute_Name;
} }

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <string> #include <string>
#include <stack>
#include "XmlDocument.h" #include "XmlDocument.h"
class XmlParser class XmlParser
@ -33,8 +34,7 @@ private:
DocumentState mDocumentState; DocumentState mDocumentState;
LineState mLineState; LineState mLineState;
XmlDocumentPtr mDocument; XmlDocumentPtr mDocument;
XmlElement* mParentElement; std::stack<XmlElement*> mWorkingElements;
XmlElement* mWorkingElement;
std::string mWorkingAttributeName; std::string mWorkingAttributeName;
std::string mWorkingTagName; std::string mWorkingTagName;
std::string mWorkingAttributeValue; std::string mWorkingAttributeValue;

View file

@ -1,6 +1,68 @@
#include "XmlWriter.h" #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;
} }

View file

@ -1,12 +1,15 @@
#pragma once #pragma once
#include "File.h"
#include "XmlDocument.h" #include "XmlDocument.h"
#include <string>
class XmlWriter class XmlWriter
{ {
public: public:
XmlWriter() = default; XmlWriter() = default;
void Write(File* file, XmlDocument* document); std::string ToString(XmlDocument* document);
private:
std::string ToString(XmlElement* element, unsigned depth=0);
}; };

View file

@ -68,3 +68,13 @@ std::size_t XmlElement::GetNumAttributes() const
{ {
return mAttributes.size(); return mAttributes.size();
} }
std::size_t XmlElement::GetNumChildren() const
{
return mChildren.size();
}
XmlElement* XmlElement::GetChild(std::size_t index) const
{
return mChildren[index].get();
}

View file

@ -1,18 +1,13 @@
#pragma once #pragma once
#include "XmlAttribute.h"
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <string> #include <string>
#include "xml-elements/XmlAttribute.h"
class XmlElement class XmlElement
{ {
protected:
std::string mTagName;
std::vector<XmlAttributeUPtr> mAttributes;
std::vector<std::unique_ptr<XmlElement> > mChildren;
std::string mText;
public: public:
XmlElement(const std::string& tagName); XmlElement(const std::string& tagName);
virtual ~XmlElement() = default; virtual ~XmlElement() = default;
@ -28,8 +23,17 @@ public:
XmlAttribute* GetAttribute(std::size_t index) const; XmlAttribute* GetAttribute(std::size_t index) const;
std::size_t GetNumAttributes() 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 SetText(const std::string& text);
void SetTagName(const std::string& tagName); 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>; using XmlElementPtr = std::shared_ptr<XmlElement>;

View file

@ -2,12 +2,11 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include "xml-elements/XmlElement.h" #include "XmlElement.h"
class XmlProlog : public XmlElement class XmlProlog : public XmlElement
{ {
public: public:
enum class Version{ enum class Version{
V1_0 V1_0
}; };

View file

@ -10,16 +10,35 @@ target_include_directories(test_utils PUBLIC
list(APPEND TestFiles list(APPEND TestFiles
audio/TestAudioWriter.cpp audio/TestAudioWriter.cpp
audio/TestMidiReader.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 list(APPEND TestNames
TestAudioWriter TestAudioWriter
TestMidiReader 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) foreach(TestFile TestName IN ZIP_LISTS TestFiles TestNames)
add_executable(${TestName} ${TestFile}) 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() endforeach()

View file

@ -8,7 +8,9 @@
#include <memory> #include <memory>
#include <string> #include <string>
#ifdef _WIN32
#include <windows.h> #include <windows.h>
#endif
#include <iostream> #include <iostream>
class TestWriteWav : public TestCase class TestWriteWav : public TestCase
@ -32,9 +34,11 @@ class TestAudioRender : public TestCase
public: public:
bool Run() override bool Run() override
{ {
#ifdef _WIN32
WasapiInterface audio_interface; WasapiInterface audio_interface;
auto device = AudioDevice::Create(); auto device = AudioDevice::Create();
audio_interface.Play(device); audio_interface.Play(device);
#endif
return true; return true;
} }
}; };
@ -42,8 +46,9 @@ public:
//int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) //int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
int main() int main()
{ {
#ifdef _WIN32
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
#endif
std::cout << "into entry point" << std::endl; std::cout << "into entry point" << std::endl;
TestCaseRunner runner; TestCaseRunner runner;
//runner.AddTestCase("TestWriteNav", std::make_unique<TestWriteWav>()); //runner.AddTestCase("TestWriteNav", std::make_unique<TestWriteWav>());
@ -51,6 +56,7 @@ int main()
const auto testsPassed = runner.Run(); const auto testsPassed = runner.Run();
return testsPassed ? 0 : -1; return testsPassed ? 0 : -1;
#ifdef _WIN32
CoUninitialize(); CoUninitialize();
#endif
} }

View 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.

View 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;
}

View 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
View file

@ -0,0 +1,8 @@
#include <dbus/dbus.h>
int main()
{
DBusError err;
DBusConnection* conn;
return 0;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}