13 import random |
13 import random |
14 |
14 |
15 from PyQt6.QtCore import QObject |
15 from PyQt6.QtCore import QObject |
16 from PyQt6.QtWidgets import QDialog |
16 from PyQt6.QtWidgets import QDialog |
17 |
17 |
18 from .Html5ToCss3ConverterParameterDialog import ( |
18 from .Html5ToCss3ConverterParameterDialog import Html5ToCss3ConverterParameterDialog |
19 Html5ToCss3ConverterParameterDialog |
|
20 ) |
|
21 |
19 |
22 |
20 |
23 class Html5ToCss3Converter(QObject): |
21 class Html5ToCss3Converter(QObject): |
24 """ |
22 """ |
25 Class implementing the HTML5 to CSS3 converter. |
23 Class implementing the HTML5 to CSS3 converter. |
26 """ |
24 """ |
|
25 |
27 CssTemplate7 = "{0}{1}{2}{3}{4}{5}{6}" |
26 CssTemplate7 = "{0}{1}{2}{3}{4}{5}{6}" |
28 CssTemplate8 = "{0}{1}{2}{3}{4}{5}{6}{7}" |
27 CssTemplate8 = "{0}{1}{2}{3}{4}{5}{6}{7}" |
29 Placeholders = ('margin:0', 'padding:0', 'border:0', 'font-size:100%', |
28 Placeholders = ( |
30 'font:inherit', 'vertical-align:baseline', 'line-height:1', |
29 "margin:0", |
31 'outline:0', 'font-weight:inherit', 'font-style:inherit', |
30 "padding:0", |
32 'font-family:inherit', 'vertical-align:baseline') |
31 "border:0", |
33 TagsToIgnore = ('head', 'meta', 'noscript', 'script', 'style', 'link', |
32 "font-size:100%", |
34 'no-js', 'title', 'object', 'col', 'colgroup', 'option', |
33 "font:inherit", |
35 'param', 'audio', 'basefont', 'isindex', 'svg', 'area', |
34 "vertical-align:baseline", |
36 'embed', 'br') |
35 "line-height:1", |
37 |
36 "outline:0", |
|
37 "font-weight:inherit", |
|
38 "font-style:inherit", |
|
39 "font-family:inherit", |
|
40 "vertical-align:baseline", |
|
41 ) |
|
42 TagsToIgnore = ( |
|
43 "head", |
|
44 "meta", |
|
45 "noscript", |
|
46 "script", |
|
47 "style", |
|
48 "link", |
|
49 "no-js", |
|
50 "title", |
|
51 "object", |
|
52 "col", |
|
53 "colgroup", |
|
54 "option", |
|
55 "param", |
|
56 "audio", |
|
57 "basefont", |
|
58 "isindex", |
|
59 "svg", |
|
60 "area", |
|
61 "embed", |
|
62 "br", |
|
63 ) |
|
64 |
38 def __init__(self, html, parent=None): |
65 def __init__(self, html, parent=None): |
39 """ |
66 """ |
40 Constructor |
67 Constructor |
41 |
68 |
42 @param html HTML text to be converted |
69 @param html HTML text to be converted |
43 @type str |
70 @type str |
44 @param parent reference to the parent object |
71 @param parent reference to the parent object |
45 @type QObject |
72 @type QObject |
46 """ |
73 """ |
47 super().__init__(parent) |
74 super().__init__(parent) |
48 |
75 |
49 self.__html = html |
76 self.__html = html |
50 |
77 |
51 def getCss3(self): |
78 def getCss3(self): |
52 """ |
79 """ |
53 Public method to get the converted CSS3 text. |
80 Public method to get the converted CSS3 text. |
54 |
81 |
55 @return CSS3 text |
82 @return CSS3 text |
56 @rtype str |
83 @rtype str |
57 """ |
84 """ |
58 dlg = Html5ToCss3ConverterParameterDialog() |
85 dlg = Html5ToCss3ConverterParameterDialog() |
59 if dlg.exec() == QDialog.DialogCode.Accepted: |
86 if dlg.exec() == QDialog.DialogCode.Accepted: |
60 indentation, placeholders = dlg.getData() |
87 indentation, placeholders = dlg.getData() |
61 |
88 |
62 self.__createSoup() |
89 self.__createSoup() |
63 |
90 |
64 alreadyDone = list(self.TagsToIgnore) |
91 alreadyDone = list(self.TagsToIgnore) |
65 |
92 |
66 css = '@charset "utf-8";{0}'.format(os.linesep) |
93 css = '@charset "utf-8";{0}'.format(os.linesep) |
67 css += "/* {0} by {1}*/{2}".format( |
94 css += "/* {0} by {1}*/{2}".format( |
68 datetime.datetime.now().isoformat().split(".")[0], |
95 datetime.datetime.now().isoformat().split(".")[0], |
69 getpass.getuser(), |
96 getpass.getuser(), |
70 2 * os.linesep |
97 2 * os.linesep, |
71 ) |
98 ) |
72 |
99 |
73 # step 1: tags |
100 # step 1: tags |
74 for tag in self.__getTags(): |
101 for tag in self.__getTags(): |
75 if tag not in alreadyDone: |
102 if tag not in alreadyDone: |
76 css += self.CssTemplate7.format( |
103 css += self.CssTemplate7.format( |
77 tag, |
104 tag, |
78 "{", |
105 "{", |
79 os.linesep, |
106 os.linesep, |
80 indentation, |
107 indentation, |
81 random.choice(self.Placeholders) + os.linesep # secok |
108 random.choice(self.Placeholders) + os.linesep # secok |
82 if placeholders else os.linesep, |
109 if placeholders |
|
110 else os.linesep, |
83 "}", |
111 "}", |
84 os.linesep |
112 os.linesep, |
85 ) |
113 ) |
86 alreadyDone.append(tag) |
114 alreadyDone.append(tag) |
87 css += "/*{0}*/{1}".format( |
115 css += "/*{0}*/{1}".format("-" * 75, os.linesep) |
88 "-" * 75, |
116 |
89 os.linesep |
|
90 ) |
|
91 |
|
92 # step 2: IDs |
117 # step 2: IDs |
93 for id_ in self.__getIds(): |
118 for id_ in self.__getIds(): |
94 if id_ not in alreadyDone: |
119 if id_ not in alreadyDone: |
95 css += "/* {0} */{1}".format( |
120 css += "/* {0} */{1}".format("_".join(id_).lower(), os.linesep) |
96 "_".join(id_).lower(), |
|
97 os.linesep |
|
98 ) |
|
99 css += self.CssTemplate8.format( |
121 css += self.CssTemplate8.format( |
100 "#", |
122 "#", |
101 id_[1], |
123 id_[1], |
102 "{", |
124 "{", |
103 os.linesep, |
125 os.linesep, |
104 indentation, |
126 indentation, |
105 random.choice(self.Placeholders) + os.linesep # secok |
127 random.choice(self.Placeholders) + os.linesep # secok |
106 if placeholders else os.linesep, |
128 if placeholders |
|
129 else os.linesep, |
107 "}", |
130 "}", |
108 os.linesep |
131 os.linesep, |
109 ) |
132 ) |
110 alreadyDone.append(id_) |
133 alreadyDone.append(id_) |
111 css += "/*{0}*/{1}".format( |
134 css += "/*{0}*/{1}".format("-" * 75, os.linesep) |
112 "-" * 75, |
135 |
113 os.linesep |
|
114 ) |
|
115 |
|
116 # step 3: classes |
136 # step 3: classes |
117 for class_ in self.__getClasses(): |
137 for class_ in self.__getClasses(): |
118 if class_ not in alreadyDone: |
138 if class_ not in alreadyDone: |
119 css += "/* {0} */{1}".format( |
139 css += "/* {0} */{1}".format("_".join(class_).lower(), os.linesep) |
120 "_".join(class_).lower(), |
|
121 os.linesep |
|
122 ) |
|
123 css += self.CssTemplate8.format( |
140 css += self.CssTemplate8.format( |
124 ".", |
141 ".", |
125 ", .".join(class_[1].split()), |
142 ", .".join(class_[1].split()), |
126 "{", |
143 "{", |
127 os.linesep, |
144 os.linesep, |
128 indentation, |
145 indentation, |
129 random.choice(self.Placeholders) + os.linesep # secok |
146 random.choice(self.Placeholders) + os.linesep # secok |
130 if placeholders else os.linesep, |
147 if placeholders |
|
148 else os.linesep, |
131 "}", |
149 "}", |
132 os.linesep |
150 os.linesep, |
133 ) |
151 ) |
134 alreadyDone.append(class_) |
152 alreadyDone.append(class_) |
135 else: |
153 else: |
136 css = "" |
154 css = "" |
137 return css.strip() |
155 return css.strip() |
138 |
156 |
139 def __createSoup(self): |
157 def __createSoup(self): |
140 """ |
158 """ |
141 Private method to get a BeaitifulSoup object with our HTML text. |
159 Private method to get a BeaitifulSoup object with our HTML text. |
142 """ |
160 """ |
143 from bs4 import BeautifulSoup |
161 from bs4 import BeautifulSoup |
|
162 |
144 self.__soup = BeautifulSoup(BeautifulSoup(self.__html).prettify()) |
163 self.__soup = BeautifulSoup(BeautifulSoup(self.__html).prettify()) |
145 |
164 |
146 def __getTags(self): |
165 def __getTags(self): |
147 """ |
166 """ |
148 Private method to extract all tags of the HTML text. |
167 Private method to extract all tags of the HTML text. |
149 |
168 |
150 @return list of all tags |
169 @return list of all tags |
151 @rtype list of str |
170 @rtype list of str |
152 """ |
171 """ |
153 tags = [t.name for t in self.__soup.find_all(True)] |
172 tags = [t.name for t in self.__soup.find_all(True)] |
154 return list(set(tags)) |
173 return list(set(tags)) |
155 |
174 |
156 def __getClasses(self): |
175 def __getClasses(self): |
157 """ |
176 """ |
158 Private method to extract all classes of the HTML text. |
177 Private method to extract all classes of the HTML text. |
159 |
178 |
160 @return list of tuples containing the tag name and its classes |
179 @return list of tuples containing the tag name and its classes |
161 as a blank separated string |
180 as a blank separated string |
162 @rtype list of tuples of (str, str) |
181 @rtype list of tuples of (str, str) |
163 """ |
182 """ |
164 classes = [(t.name, " ".join(t["class"])) for t in |
183 classes = [ |
165 self.__soup.find_all(True, {"class": True})] |
184 (t.name, " ".join(t["class"])) |
|
185 for t in self.__soup.find_all(True, {"class": True}) |
|
186 ] |
166 return sorted(set(classes)) |
187 return sorted(set(classes)) |
167 |
188 |
168 def __getIds(self): |
189 def __getIds(self): |
169 """ |
190 """ |
170 Private method to extract all IDs of the HTML text. |
191 Private method to extract all IDs of the HTML text. |
171 |
192 |
172 @return list of tuples containing the tag name and its ID |
193 @return list of tuples containing the tag name and its ID |
173 @rtype list of tuples of (str, str) |
194 @rtype list of tuples of (str, str) |
174 """ |
195 """ |
175 ids = [(t.name, t["id"]) for t in |
196 ids = [(t.name, t["id"]) for t in self.__soup.find_all(True, {"id": True})] |
176 self.__soup.find_all(True, {"id": True})] |
|
177 return sorted(set(ids)) |
197 return sorted(set(ids)) |