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