Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes relative paths #17

Merged
merged 1 commit into from
Oct 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed data/el_aleph.epub
Binary file not shown.
Binary file added data/poesia_aPizz.epub
Binary file not shown.
Binary file added fixtures/relative_paths.epub
Binary file not shown.
87 changes: 70 additions & 17 deletions lib/Epub/EpubList/Epub.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,18 @@
#ifndef UNIT_TEST
#include <esp_log.h>
#else
#define ESP_LOGI(args...)
#define ESP_LOGE(args...)
#define ESP_LOGD(args...)
#define ESP_LOGI(tag, args...) \
printf(args); \
printf("\n");
#define ESP_LOGE(tag, args...) \
printf(args); \
printf("\n");
#define ESP_LOGD(tag, args...) \
printf(args); \
printf("\n");
#define ESP_LOGW(tag, args...) \
printf(args); \
printf("\n");
#endif
#include <map>
#include "tinyxml2.h"
Expand Down Expand Up @@ -113,12 +122,15 @@ bool Epub::load()
}
m_title = title->GetText();
auto cover = metadata->FirstChildElement("meta");
while (cover && cover->Attribute("name") && strcmp(cover->Attribute("name"), "cover") != 0)
{
cover = cover->NextSiblingElement("meta");
}
if (!cover)
{
ESP_LOGE(TAG, "Missing cover");
return false;
ESP_LOGW(TAG, "Missing cover");
}
auto cover_item = cover->Attribute("content");
auto cover_item = cover ? cover->Attribute("content") : nullptr;
// read the manifest and spine
// the manifest gives us the names of the files
// the spine gives us the order of the files
Expand All @@ -135,9 +147,9 @@ bool Epub::load()
while (item)
{
std::string item_id = item->Attribute("id");
std::string href = item->Attribute("href");
std::string href = m_base_path + item->Attribute("href");
// grab the cover image
if (item_id == cover_item)
if (cover_item && item_id == cover_item)
{
m_cover_image_item = href;
}
Expand Down Expand Up @@ -180,10 +192,57 @@ int Epub::get_spine_items_count()
return m_spine.size();
}

std::string normalise_path(const std::string &path)
{
std::vector<std::string> components;
std::string component;
for (auto c : path)
{
if (c == '/')
{
if (!component.empty())
{
if (component == "..")
{
if (!components.empty())
{
components.pop_back();
}
}
else
{
components.push_back(component);
}
component.clear();
}
}
else
{
component += c;
}
}
if (!component.empty())
{
components.push_back(component);
}
std::string result;
for (auto &component : components)
{
if (result.size() > 0)
{
result += "/";
}
result += component;
}
return result;
}

uint8_t *Epub::get_item_contents(const std::string &item_href, size_t *size)
{
ZipFile zip(m_path.c_str());
auto content = zip.read_file_to_memory((m_base_path + item_href).c_str(), size);
std::string path = normalise_path(item_href);
ESP_LOGI(TAG, "Reading item %s", path.c_str());
auto content = zip.read_file_to_memory(path.c_str(), size);
if (!content)
{
ESP_LOGE(TAG, "Failed to read item");
Expand All @@ -192,13 +251,7 @@ uint8_t *Epub::get_item_contents(const std::string &item_href, size_t *size)
return content;
}

char *Epub::get_spine_item_contents(int spine_index)
std::string &Epub::get_spine_item(int spine_index)
{
if (spine_index < 0 || spine_index >= m_spine.size())
{
ESP_LOGE(TAG, "Invalid spine_index %d", spine_index);
return nullptr;
}
ESP_LOGD(TAG, "Loading Section: %s", m_spine[spine_index].c_str());
return (char *)get_item_contents(m_spine[spine_index].c_str());
return m_spine[spine_index];
}
3 changes: 2 additions & 1 deletion lib/Epub/EpubList/Epub.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@ class Epub
public:
Epub(const std::string &path);
~Epub() {}
std::string &get_base_path() { return m_base_path; }
bool load();
const std::string &get_path() const { return m_path; }
const std::string &get_title();
const std::string &get_cover_image_item();
uint8_t *get_item_contents(const std::string &item_href, size_t *size = nullptr);
char *get_spine_item_contents(int spine_index);
std::string &get_spine_item(int spine_index);
int get_spine_items_count();
};
9 changes: 4 additions & 5 deletions lib/Epub/EpubList/EpubList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,8 @@ bool EpubList::load(const char *path)
Epub *epub = new Epub(std::string("/fs/") + ent->d_name);
if (epub->load())
{
strncpy(state->epub_list[state->num_epubs].title, epub->get_title().c_str(), MAX_TITLE_SIZE);
strncpy(state->epub_list[state->num_epubs].path, epub->get_path().c_str(), MAX_PATH_SIZE);
strncpy(state->epub_list[state->num_epubs].cover_item, epub->get_cover_image_item().c_str(), MAX_PATH_SIZE);
strncpy(state->epub_list[state->num_epubs].title, epub->get_title().c_str(), MAX_TITLE_SIZE);
state->num_epubs++;
}
else
Expand Down Expand Up @@ -174,15 +173,15 @@ void EpubList::render()
int image_height = cell_height - PADDING * 2;
int image_width = 2 * image_height / 3;
size_t image_data_size = 0;
uint8_t *image_data = epub->get_item_contents(state->epub_list[i].cover_item, &image_data_size);
renderer->draw_image(state->epub_list[i].cover_item, image_data, image_data_size, image_xpos, image_ypos, image_width, image_height);
uint8_t *image_data = epub->get_item_contents(epub->get_cover_image_item(), &image_data_size);
renderer->draw_image(epub->get_cover_image_item(), image_data, image_data_size, image_xpos, image_ypos, image_width, image_height);
free(image_data);
// draw the title
int text_xpos = image_xpos + image_width + PADDING;
int text_ypos = ypos + PADDING;
int text_width = renderer->get_page_width() - (text_xpos + PADDING);
int text_height = cell_height - PADDING * 2;
renderer->draw_text_box(state->epub_list[i].title, text_xpos, text_ypos, text_width, text_height);
renderer->draw_text_box(epub->get_title(), text_xpos, text_ypos, text_width, text_height);
delete epub;
}
// clear the selection box around the previous selected item
Expand Down
1 change: 0 additions & 1 deletion lib/Epub/EpubList/EpubList.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ typedef struct
{
char path[MAX_PATH_SIZE];
char title[MAX_TITLE_SIZE];
char cover_item[MAX_PATH_SIZE];
} EpubListItem;

typedef struct
Expand Down
6 changes: 4 additions & 2 deletions lib/Epub/EpubList/EpubReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,11 @@ void EpubReader::parse_and_layout_current_section()
renderer->show_busy();
ESP_LOGD(TAG, "Parse and render section %d", state.current_section);
ESP_LOGD(TAG, "Before read html: %d", esp_get_free_heap_size());
char *html = epub->get_spine_item_contents(state.current_section);
std::string item = epub->get_spine_item(state.current_section);
std::string base_path = item.substr(0, item.find_last_of('/') + 1);
char *html = reinterpret_cast<char *>(epub->get_item_contents(item));
ESP_LOGD(TAG, "After read html: %d", esp_get_free_heap_size());
parser = new RubbishHtmlParser(html, strlen(html));
parser = new RubbishHtmlParser(html, strlen(html), base_path);
free(html);
ESP_LOGD(TAG, "After parse: %d", esp_get_free_heap_size());
parser->layout(renderer, epub);
Expand Down
5 changes: 3 additions & 2 deletions lib/Epub/RubbishHtmlParser/RubbishHtmlParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,9 @@ bool matches(const char *tag_name, const char *possible_tags[], int possible_tag
return false;
}

RubbishHtmlParser::RubbishHtmlParser(const char *html, int length)
RubbishHtmlParser::RubbishHtmlParser(const char *html, int length, const std::string &base_path)
{
m_base_path = base_path;
parse(html, length);
}

Expand Down Expand Up @@ -84,7 +85,7 @@ bool RubbishHtmlParser::VisitEnter(const tinyxml2::XMLElement &element, const ti
delete currentTextBlock;
currentTextBlock = nullptr;
}
blocks.push_back(new ImageBlock(src));
blocks.push_back(new ImageBlock(m_base_path + src));
// start a new text block
startNewTextBlock();
}
Expand Down
4 changes: 3 additions & 1 deletion lib/Epub/RubbishHtmlParser/RubbishHtmlParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ class RubbishHtmlParser : public tinyxml2::XMLVisitor
TextBlock *currentTextBlock = nullptr;
std::vector<Page *> pages;

std::string m_base_path;

// start a new text block if needed
void startNewTextBlock();

public:
RubbishHtmlParser(const char *html, int length);
RubbishHtmlParser(const char *html, int length, const std::string &base_path);
~RubbishHtmlParser();

// xml parser callbacks
Expand Down
8 changes: 4 additions & 4 deletions lib/Epub/RubbishHtmlParser/blocks/ImageBlock.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@
#ifndef UNIT_TEST
#include <esp_log.h>
#else
#define ESP_LOGE(args...)
#define ESP_LOGI(args...)
#define ESP_LOGE(args...)
#define ESP_LOGD(args...)
#define ESP_LOGW(args...)
#endif

class ImageBlock : public Block
{
private:
public:
// the src attribute from the image element
std::string m_src;

public:
int y_pos;
int x_pos;
int width;
Expand Down
14 changes: 11 additions & 3 deletions lib/Epub/RubbishHtmlParser/blocks/TextBlock.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@
#include "../../Renderer/Renderer.h"
#include <limits.h>
#include "Block.h"
#ifndef UNIT_TEST
#include <esp_log.h>
#else
#define ESP_LOGI(args...)
#define ESP_LOGE(args...)
#define ESP_LOGD(args...)
#define ESP_LOGW(args...)
#endif

typedef enum
{
Expand All @@ -11,13 +19,13 @@ typedef enum
} SPAN_STYLES;

// TODO - is there any more whitespace we should consider?
bool is_whitespace(char c)
static bool is_whitespace(char c)
{
return (c == ' ' || c == '\r' || c == '\n');
}

// move past anything that should be considered part of a work
int skip_word(const char *text, int index, int length)
static int skip_word(const char *text, int index, int length)
{
while (index < length && !is_whitespace(text[index]))
{
Expand All @@ -27,7 +35,7 @@ int skip_word(const char *text, int index, int length)
}

// skip past any white space characters
int skip_whitespace(const char *html, int index, int length)
static int skip_whitespace(const char *html, int index, int length)
{
while (index < length && is_whitespace(html[index]))
{
Expand Down
2 changes: 1 addition & 1 deletion lib/epdiy/epdiy
Submodule epdiy updated 1 files
+1 −1 src/epd_driver/font.c
29 changes: 25 additions & 4 deletions test/rubbish_html_parser.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
#include <unity.h>
#include <string.h>
#include <RubbishHtmlParser/RubbishHtmlParser.h>
#include <RubbishHtmlParser/blocks/Block.h>
#include <RubbishHtmlParser/RubbishHtmlParser.h>
#include <RubbishHtmlParser/blocks/ImageBlock.h>
#include <Renderer/Renderer.h>
#include <EpubList/Epub.h>
#include <iterator>

class TestRenderer : public Renderer
{
Expand Down Expand Up @@ -39,10 +42,28 @@ void test_parser(void)
"<p>Some more text</p>"
"<div>A block of text</div>"
"<h2>A sub heading</h2>"
"<img src=\"test.png\" />"
"<p>Bananas!</p>"
"</body>"
"</html>";
RubbishHtmlParser parser(html, strlen(html));
parser.layout(new TestRenderer(), nullptr);
TEST_ASSERT_EQUAL(7, parser.get_blocks().size());
{
RubbishHtmlParser parser(html, strlen(html), "");
parser.layout(new TestRenderer(), new Epub("test"));
TEST_ASSERT_EQUAL(8, parser.get_blocks().size());
auto iterator = parser.get_blocks().begin();
std::advance(iterator, 5);
Block *img_block = *iterator;
TEST_ASSERT_EQUAL(BlockType::IMAGE_BLOCK, img_block->getType());
TEST_ASSERT_EQUAL_STRING("test.png", reinterpret_cast<ImageBlock *>(img_block)->m_src.c_str());
}
{
RubbishHtmlParser parser(html, strlen(html), "HTML/");
parser.layout(new TestRenderer(), new Epub("test"));
TEST_ASSERT_EQUAL(8, parser.get_blocks().size());
auto iterator = parser.get_blocks().begin();
std::advance(iterator, 5);
Block *img_block = *iterator;
TEST_ASSERT_EQUAL(BlockType::IMAGE_BLOCK, img_block->getType());
TEST_ASSERT_EQUAL_STRING("HTML/test.png", reinterpret_cast<ImageBlock *>(img_block)->m_src.c_str());
}
}
20 changes: 17 additions & 3 deletions test/test_epub_load.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,22 @@ void test_epub_no_oebps_load(void)
TEST_ASSERT_TRUE_MESSAGE(result, "Epub load failed");
TEST_ASSERT_EQUAL_STRING("El Aleph", epub->get_title().c_str());
TEST_ASSERT_EQUAL(2, epub->get_spine_items_count());
TEST_ASSERT_NOT_NULL_MESSAGE(epub->get_spine_item_contents(0), "No content for first chapter");
TEST_ASSERT_NOT_NULL_MESSAGE(epub->get_spine_item_contents(1), "No content for second chapter");
TEST_ASSERT_NOT_NULL_MESSAGE(epub->get_item_contents(epub->get_spine_item(0)), "No content for first chapter");
TEST_ASSERT_NOT_NULL_MESSAGE(epub->get_item_contents(epub->get_spine_item(1)), "No content for second chapter");
}

void test_epub_relative_image_paths(void)
{
Epub *epub = new Epub("fixtures/relative_paths.epub");
bool result = epub->load();
TEST_ASSERT_TRUE_MESSAGE(result, "Epub load failed");
TEST_ASSERT_EQUAL(373, epub->get_spine_items_count());
for (int i = 0; i < 373; i++)
{
TEST_ASSERT_NOT_NULL_MESSAGE(epub->get_item_contents(epub->get_spine_item(i)), "No content for chapter");
}
TEST_ASSERT_EQUAL_STRING("Images/cover.jpg", epub->get_cover_image_item().c_str());
TEST_ASSERT_NOT_NULL(epub->get_item_contents(epub->get_cover_image_item()));
}

void test_epub_load(void)
Expand All @@ -21,7 +35,7 @@ void test_epub_load(void)
TEST_ASSERT_EQUAL(13, epub->get_spine_items_count());
for (int i = 0; i < 13; i++)
{
TEST_ASSERT_NOT_NULL_MESSAGE(epub->get_spine_item_contents(i), "No content for chapter");
TEST_ASSERT_NOT_NULL_MESSAGE(epub->get_item_contents(epub->get_spine_item(i)), "No content for chapter");
}
TEST_ASSERT_EQUAL_STRING("@public@vhost@g@gutenberg@html@files@43@43-h@images@cover.jpg", epub->get_cover_image_item().c_str());
TEST_ASSERT_NOT_NULL(epub->get_item_contents(epub->get_cover_image_item()));
Expand Down
2 changes: 2 additions & 0 deletions test/tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ void test_xml_parser(void);
void test_parser(void);
void test_epub_no_oebps_load(void);
void test_epub_load(void);
void test_epub_relative_image_paths(void);

int main(int argc, char **argv)
{
Expand All @@ -12,6 +13,7 @@ int main(int argc, char **argv)
RUN_TEST(test_parser);
RUN_TEST(test_epub_no_oebps_load);
RUN_TEST(test_epub_load);
RUN_TEST(test_epub_relative_image_paths);
UNITY_END();

return 0;
Expand Down