summaryrefslogtreecommitdiff
path: root/include/htmlrenderer.h
blob: 3ed946959fbe7888cf4b0291f0e44454de87b078 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#ifndef NEWSBOAT_HTMLRENDERER_H_
#define NEWSBOAT_HTMLRENDERER_H_

#include <istream>
#include <map>
#include <string>
#include <vector>

#include "textformatter.h"

namespace newsboat {

class TagSoupPullParser;

// This enum has to be kept in sync with enum LinkType in rust/libnewsboat/src/htmlrenderer.rs
enum class LinkType { HREF, IMG, EMBED, IFRAME, VIDEO, AUDIO };

enum class HtmlTag {
	A = 1,
	EMBED,
	IFRAME,
	BR,
	PRE,
	ITUNESHACK,
	IMG,
	BLOCKQUOTE,
	H1,
	H2,
	H3,
	H4,
	H5,
	H6,
	P,
	DIV,
	OL,
	UL,
	LI,
	DT,
	DD,
	DL,
	SUP,
	SUB,
	HR,
	STRONG,
	UNDERLINE,
	QUOTATION,
	SCRIPT,
	STYLE,
	TABLE,
	TH,
	TR,
	TD,
	VIDEO,
	AUDIO,
	SOURCE
};

typedef std::pair<std::string, LinkType> LinkPair;

class HtmlRenderer {
public:
	explicit HtmlRenderer(bool raw = false);
	void render(const std::string& source,
		std::vector<std::pair<LineType, std::string>>& lines,
		std::vector<LinkPair>& links,
		const std::string& url);
	void render(std::istream& input,
		std::vector<std::pair<LineType, std::string>>& lines,
		std::vector<LinkPair>& links,
		const std::string& url);
	static std::string render_hr(const unsigned int width);
	// only public for unit testing purposes:
	std::string format_ol_count(unsigned int count, char type);

	struct TableCell {
		explicit TableCell(size_t s)
			: span(s)
		{
		}
		size_t span;
		std::vector<std::string> text; // multiline cell text
	};

	struct TableRow {
		TableRow()
			: inside(false)
		{
		}

		void add_text(const std::string& str);
		void start_cell(size_t span);
		void complete_cell();

		bool inside; // inside a cell
		std::vector<TableCell> cells;
	};

	struct Table {
		explicit Table(bool b)
			: inside(false)
			, has_border(b)
		{
		}

		void add_text(const std::string& str);
		void start_row();
		void complete_row();
		void start_cell(size_t span);
		void complete_cell();

		bool inside; // inside a row
		bool has_border;
		std::vector<TableRow> rows;
	};

private:
	void prepare_new_line(std::string& line, int indent_level);
	bool line_is_nonempty(const std::string& line);
	unsigned int add_link(std::vector<LinkPair>& links,
		const std::string& link,
		LinkType type);
	std::string absolute_url(const std::string& url,
		const std::string& link);
	std::string type2str(LinkType type);
	std::map<std::string, HtmlTag> tags;
	void render_table(const Table& table,
		std::vector<std::pair<LineType, std::string>>& lines);
	void add_nonempty_line(const std::string& curline,
		std::vector<Table>& tables,
		std::vector<std::pair<LineType, std::string>>& lines);
	void add_line(const std::string& curline,
		std::vector<Table>& tables,
		std::vector<std::pair<LineType, std::string>>& lines);
	void add_line_softwrappable(const std::string& line,
		std::vector<std::pair<LineType, std::string>>& lines);
	void add_line_nonwrappable(const std::string& line,
		std::vector<std::pair<LineType, std::string>>& lines);
	void add_hr(std::vector<std::pair<LineType, std::string>>& lines);
	std::string get_char_numbering(unsigned int count);
	std::string get_roman_numbering(unsigned int count);
	void add_media_link(std::string& line, std::vector<LinkPair>& links,
		const std::string& url, const std::string& media_url,
		const std::string& media_title, unsigned int media_count,
		LinkType type);
	HtmlTag extract_tag(TagSoupPullParser& parser);
	bool raw_;
};

} // namespace newsboat

#endif /* NEWSBOAT_HTMLRENDERER_H_ */