Skip to content

Commit 4e2d03f

Browse files
committed
Add OUTPUT_FULL
1 parent 04b0a07 commit 4e2d03f

File tree

10 files changed

+245
-23
lines changed

10 files changed

+245
-23
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,5 +169,5 @@ cython_debug/
169169
flask_session/
170170
/*_session/
171171

172-
output_full.py
172+
sketch*.py
173173
####################################################

README.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@ Once a web project is done, it's time to document your hard work and show it off
99
As of v2.2.0, the following `OUTPUT_` types can be generated:
1010

1111
* `OUTPUT_SCREENSHOTS`
12-
* Four image files of screenshots taken in different window sizes mimicking the viewport of a desktop (2160x1360), a laptop (1440x900), a tablet (768x1024) and a smartphone (230x490).
12+
* Four image files of screenshots taken in different window sizes mimicking the viewport of a desktop (2160x1360), a laptop (1440x900), a tablet (768x1024) and a smartphone (230x490). And one image file of a full-page screenshot.
1313
* `OUTPUT_MAIN`
1414
* An image file of those screenshots overlaid on top schematic diagrams and then collaged together. (See below for an example ouput.)
1515
* `OUTPUT_BROWSER`
1616
* An image file of the desktop screenshot overlaid on top of a schematic diagram. (Scroll to the bottom for an example ouput.)
1717
* `OUTPUT_MOBILES`
1818
* An image file of the tablet and smartphone screenshots overlaid on top of a schematic diagram and paired together. (Scroll to the bottom for an example ouput.)
19+
* `OUTPUT_FULL`
20+
* An image file of the full-pge screenshots overlaid on top of a schematic diagram. (Scroll to the bottom for an example ouput.)
1921

2022
More coming soon!
2123

@@ -78,7 +80,7 @@ This is a list of parameters you can change in `local_settings.py`.
7880

7981
### Example
8082

81-
This code will portfoliofy a webpage served on a web server running on a local computer. It only requests `OUTPUT_BROWSER` in the default PNG format. `OUTPUT_MAIN` and `OUTPUT_MOBILES` will not be processed. But all `OUTPUT_SCREENSHOTS` will be saved. All other parameters remain set to their default values.
83+
This code will portfoliofy a webpage served on a web server running on a local computer. It only requests `OUTPUT_BROWSER` in the default PNG format. `OUTPUT_MAIN`, `OUTPUT_MOBILES` and `OUTPUT_FULL` will not be processed. But all `OUTPUT_SCREENSHOTS` will be saved. All other parameters remain set to their default values.
8284

8385
```python
8486
user_input = {
@@ -112,6 +114,15 @@ user_input = {
112114
"base_stroke_color": "#23445D",
113115
"base_fill_color": "#BAC8D3",
114116
},
117+
"output_full": {
118+
"request": False,
119+
"format": "png",
120+
"doc_pad_h": 300,
121+
"doc_pad_v": 200,
122+
"doc_fill_color": "#FFFFFF",
123+
"base_stroke_color": "#23445D",
124+
"base_fill_color": "#BAC8D3",
125+
},
115126
}
116127
```
117128

@@ -150,3 +161,5 @@ New features development is ongoing.
150161
![Portfoliofy](/images/portfoliofy2.png)
151162

152163
![Portfoliofy](/images/portfoliofy3.png)
164+
165+
![Portfoliofy](/images/portfoliofy4.png)

helpers.py

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import os
2+
import json
3+
import base64
24
from selenium import webdriver
35
from PIL import Image
46
from time import sleep
@@ -26,7 +28,70 @@ def get_screenshot(remote_url, wait, directory_path, input):
2628
# Take screenshot
2729
driver.close()
2830

29-
return f"{input['filename_large']} screenshot generated."
31+
return 1
32+
33+
34+
def get_screenshot_full(remote_url, wait, directory_path, input):
35+
36+
options = Options()
37+
options.add_argument(f"--no-sandbox")
38+
options.add_argument(f"--headless=new")
39+
options.add_argument(f"--hide-scrollbars")
40+
41+
driver = webdriver.Chrome(options=options)
42+
43+
driver.get(remote_url)
44+
45+
sleep(wait)
46+
47+
filename_output_full_screenshot_png = input["filename_large"]
48+
49+
png = get_screenshot_full_chrome(driver)
50+
with open(f"{directory_path}/{filename_output_full_screenshot_png}", 'wb') as f:
51+
f.write(png)
52+
53+
driver.close()
54+
55+
return 1
56+
57+
58+
def get_screenshot_full_chrome(driver) :
59+
60+
# Function adapted from StackOverflow answer
61+
# https://stackoverflow.com/questions/45199076/take-full-page-screenshot-in-chrome-with-selenium/45201692#45201692
62+
63+
def send(cmd, params):
64+
resource = "/session/%s/chromium/send_command_and_get_result" % \
65+
driver.session_id
66+
url = driver.command_executor._url + resource
67+
body = json.dumps({'cmd':cmd, 'params': params})
68+
response = driver.command_executor._request('POST', url, body)
69+
return response.get('value')
70+
71+
def evaluate(script):
72+
response = send('Runtime.evaluate', {
73+
'returnByValue': True,
74+
'expression': script
75+
})
76+
return response['result']['value']
77+
78+
metrics = evaluate( \
79+
"({" + \
80+
"width: Math.max(window.innerWidth, document.body.scrollWidth, " + \
81+
"document.documentElement.scrollWidth)|0," + \
82+
"height: Math.max(innerHeight, document.body.scrollHeight, " + \
83+
"document.documentElement.scrollHeight)|0," + \
84+
"deviceScaleFactor: window.devicePixelRatio || 1," + \
85+
"mobile: typeof window.orientation !== 'undefined'" + \
86+
"})")
87+
send('Emulation.setDeviceMetricsOverride', metrics)
88+
screenshot = send('Page.captureScreenshot', {
89+
'format': 'png',
90+
'fromSurface': True
91+
})
92+
send('Emulation.clearDeviceMetricsOverride', {})
93+
94+
return base64.b64decode(screenshot['data'])
3095

3196

3297
def get_screenshot_resized(directory_main, directory_screenshots, filename_input, filename_output, new_width, height_crop):
@@ -51,7 +116,7 @@ def get_screenshot_resized(directory_main, directory_screenshots, filename_input
51116
# Save the cropped image
52117
cropped_image.save(f"{directory_main}/{filename_output}")
53118

54-
return f"{filename_output} screenshot generated."
119+
return 1
55120

56121

57122
def get_screenshot_resized_overlaid(base, overlay, lat, lng, directory_path, new_file_name):

images/portfoliofy1.png

1.33 MB
Loading

images/portfoliofy2.png

642 KB
Loading

images/portfoliofy3.png

287 KB
Loading

images/portfoliofy4.png

1.41 MB
Loading

local_settings.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@
2929
"base_stroke_color": "#23445D",
3030
"base_fill_color": "#BAC8D3",
3131
},
32+
"output_full": {
33+
"request": True,
34+
"format": "png",
35+
"doc_pad_h": 300,
36+
"doc_pad_v": 200,
37+
"doc_fill_color": "#FFFFFF",
38+
"base_stroke_color": "#23445D",
39+
"base_fill_color": "#BAC8D3",
40+
},
3241
}
3342

3443

@@ -90,4 +99,17 @@
9099
"height_small": None,
91100
"small_height_crop": 490,
92101
},
102+
"full": {
103+
"filename_large": "screenshot_full_large.png",
104+
"width_large": 2160,
105+
"height_large": 1360,
106+
"filename_medium": "screenshot_full_medium.png",
107+
"width_medium": 2048,
108+
"height_medium": None,
109+
"medium_height_crop": 1152,
110+
"filename_small": "screenshot_full_small.png",
111+
"width_small": 768,
112+
"height_small": None,
113+
"small_height_crop": 1024,
114+
},
93115
}

output_full.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
from PIL import Image, ImageOps
2+
from cairosvg import svg2png
3+
from textwrap import dedent
4+
from helpers import get_output_padded, cleanup
5+
6+
7+
def process_request_full(blueprint_user, blueprint_system, directory_main, directory_screenshots):
8+
9+
full = blueprint_system.get("full")
10+
11+
# Set svg for tablet
12+
svg_full = dedent(dedent(dedent(f'''\
13+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="776px"
14+
height="121px" viewBox="-0.5 -0.5 776 121" style="background-color: rgb(255, 255, 255);">
15+
<defs />
16+
<g>
17+
<rect x="2" y="2" width="772" height="117" rx="17.55" ry="17.55" fill="#bac8d3" stroke="#23445d"
18+
stroke-width="4" pointer-events="all" />
19+
<ellipse cx="41" cy="31" rx="9" ry="9" fill="#ffffff" stroke="#23445d" stroke-width="2" pointer-events="all" />
20+
<ellipse cx="73" cy="31" rx="9" ry="9" fill="#ffffff" stroke="#23445d" stroke-width="2" pointer-events="all" />
21+
<ellipse cx="105" cy="31" rx="9" ry="9" fill="#ffffff" stroke="#23445d" stroke-width="2" pointer-events="all" />
22+
</g>
23+
</svg>'''
24+
)))
25+
26+
# Create base - full
27+
filename_output_full_base_svg = "output_full_base.svg"
28+
filename_output_full_base_png = "output_full_base.png"
29+
get_output_mobiles_base(blueprint_user, directory_main, svg_full, filename_output_full_base_svg, filename_output_full_base_png)
30+
31+
# Create overlay - full - temp
32+
filename_input = blueprint_system["filename_large"]
33+
filename_output = filename_output_full_overlay_png = "output_full_overlay.png"
34+
get_output_full_overlay_resized(blueprint_system, directory_main, directory_screenshots, filename_input, filename_output)
35+
get_output_full_overlay_bordered(blueprint_user, directory_main, filename_output)
36+
print("OUTPUT_FULL - overlay - generated.")
37+
38+
output_full_base = Image.open(f"{directory_main}/{filename_output_full_base_png}")
39+
output_full_overlay = Image.open(f"{directory_main}/{filename_output_full_overlay_png}")
40+
41+
width = 776
42+
height = output_full_overlay.height + 60
43+
44+
output_full_final = Image.new("RGB", (width, height), (255, 255, 255))
45+
46+
output_full_final.paste(output_full_base, (0,0))
47+
output_full_final.paste(output_full_overlay, (0,60))
48+
49+
filename_output_full_temp = "output_full_temp.png"
50+
output_full_final.save(f"{directory_main}/{filename_output_full_temp}")
51+
52+
# Add padding
53+
right = blueprint_user["doc_pad_h"]
54+
left = blueprint_user["doc_pad_h"]
55+
top = blueprint_user["doc_pad_v"]
56+
bottom = blueprint_user["doc_pad_v"]
57+
color = blueprint_user["doc_fill_color"]
58+
filename_output_browser_final = "output_full_final.png"
59+
get_output_padded(directory_main, filename_output_full_temp, filename_output_browser_final, right, left, top, bottom, color)
60+
print("OUTPUT_FULL - final - generated.")
61+
62+
# Delete temp files
63+
cleanup(directory_main, filename_output_full_base_svg)
64+
cleanup(directory_main, filename_output_full_base_png)
65+
cleanup(directory_main, filename_output_full_overlay_png)
66+
cleanup(directory_main, filename_output_full_temp)
67+
68+
return 1
69+
70+
71+
def get_output_mobiles_base(blueprint, directory_main, svg, svg_filename, png_filename):
72+
73+
# Create SVG file
74+
with open(f"{directory_main}/{svg_filename}", "w") as file:
75+
file.write(svg)
76+
77+
# Convert to SVG to PNG
78+
svg2png(url=f"{directory_main}/{svg_filename}", write_to=f"{directory_main}/{png_filename}", background_color=blueprint["doc_fill_color"])
79+
80+
return 1
81+
82+
83+
def get_output_full_overlay_resized(blueprint, directory_main, directory_screenshots, filename_input, filename_output):
84+
85+
image = Image.open(f"{directory_screenshots}/{filename_input}")
86+
87+
aspect_ratio = image.height / image.width
88+
89+
new_height = int(blueprint["width_small"] * aspect_ratio)
90+
91+
# Resize the image
92+
resized_image = image.resize((blueprint["width_small"], new_height))
93+
94+
resized_image.save(f"{directory_main}/{filename_output}")
95+
96+
return 1
97+
98+
99+
def get_output_full_overlay_bordered(blueprint, directory_main, filename_input):
100+
101+
# Open the PNG image
102+
image = Image.open(f"{directory_main}/{filename_input}")
103+
104+
# Set the border width and color
105+
border_width = 4
106+
border_color = blueprint["base_stroke_color"]
107+
108+
# Add the border to the image
109+
image_with_border = ImageOps.expand(image, border=border_width, fill=border_color)
110+
111+
# Save the image with the border
112+
image_with_border.save(f"{directory_main}/{filename_input}")
113+
114+
return 1
115+
116+

portfoliofy.py

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
from datetime import datetime
44
import requests
55
from local_settings import user_input, system_input
6-
from helpers import get_screenshot
6+
from helpers import get_screenshot, get_screenshot_full
77
from output_browser import process_request_browser
88
from output_main import process_request_main
99
from output_mobiles import process_request_mobiles
10+
from output_full import process_request_full
1011

1112

1213
def main():
@@ -20,6 +21,7 @@ def main():
2021
output_main = user_input.get("output_main")
2122
output_browser = user_input.get("output_browser")
2223
output_mobiles = user_input.get("output_mobiles")
24+
output_full = user_input.get("output_full")
2325

2426
status_code = get_url(remote_url)
2527

@@ -49,62 +51,66 @@ def main():
4951
laptop = system_input.get("laptop")
5052
tablet = system_input.get("tablet")
5153
smartphone = system_input.get("smartphone")
54+
full = system_input.get("full")
5255

53-
print(get_screenshot(remote_url, wait, directory_screenshots, desktop))
54-
print(get_screenshot(remote_url, wait, directory_screenshots, laptop))
55-
print(get_screenshot(remote_url, wait, directory_screenshots, tablet))
56-
print(get_screenshot(remote_url, wait, directory_screenshots, smartphone))
56+
get_screenshot(remote_url, wait, directory_screenshots, desktop)
57+
get_screenshot(remote_url, wait, directory_screenshots, laptop)
58+
get_screenshot(remote_url, wait, directory_screenshots, tablet)
59+
get_screenshot(remote_url, wait, directory_screenshots, smartphone)
60+
get_screenshot_full(remote_url, wait, directory_screenshots, full)
5761

5862
# ################################################## #
5963
# Process Request - OUTPUT_MAIN
6064
# ################################################## #
6165
if output_main["request"] == True:
62-
63-
result = process_request_main(
64-
output_main, system_input, directory_main, directory_screenshots)
66+
result = process_request_main(output_main, system_input, directory_main, directory_screenshots)
6567

6668
if result == 1:
6769
print("OUTPUT_MAIN request processed.")
6870

6971
else:
70-
7172
print("OUTPUT_MAIN not requested.")
7273

7374
# ################################################## #
7475
# Process Request - OUPUT_BROWSER
7576
# ################################################## #
7677
if output_browser["request"] == True:
77-
78-
result = process_request_browser(
79-
output_browser, desktop, directory_main, directory_screenshots)
78+
result = process_request_browser(output_browser, desktop, directory_main, directory_screenshots)
8079

8180
if result == 1:
8281
print("OUTPUT_BROWSER request processed.")
8382

8483
else:
85-
8684
print("OUTPUT_BROWSER not requested.")
8785

8886
# ################################################## #
8987
# Process Request - OUPUT_MOBILES
9088
# ################################################## #
9189
if output_mobiles["request"] == True:
92-
93-
result = process_request_mobiles(
94-
output_mobiles, system_input, directory_main, directory_screenshots)
90+
result = process_request_mobiles(output_mobiles, system_input, directory_main, directory_screenshots)
9591

9692
if result == 1:
9793
print("OUTPUT_MOBILES request processed.")
9894

9995
else:
100-
10196
print("OUTPUT_MOBILES not requested.")
10297

98+
# ################################################## #
99+
# Process Request - OUPUT_FULL
100+
# ################################################## #
101+
if output_full["request"] == True:
102+
result = process_request_full(output_full, full, directory_main, directory_screenshots)
103+
104+
if result == 1:
105+
print("OUTPUT_FULL request processed.")
106+
107+
else:
108+
print("OUTPUT_FULL not requested.")
109+
103110
# ################################################## #
104111
# Process Request - OUPUT_SCREENSHOTS
105112
# ################################################## #
106113
if screenshots == False:
107-
108114
shutil.rmtree(directory_screenshots)
109115
print("Screenshots directory deleted.")
110116

0 commit comments

Comments
 (0)