Commit 7702d641 authored by Antoine's avatar Antoine

py: toc

parent b22ddcdc
Pipeline #155153806 passed with stage
in 35 seconds
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "compile",
"type": "shell",
"command": "python ~/repo/antoine-jaunard/main.py",
"problemMatcher": [],
"group": {
"kind": "build",
"isDefault": true
}
}
]
}
\ No newline at end of file
---
title: Machine design
---
TODO: everything :)
\ No newline at end of file
---
title: Interactive plant
tags: toc
---
# Introduction
A device that helps humans communicate with plants.
# External resources
[Kanban board](btn:http://board.antoine.studio/?controller=BoardViewController&action=readonly&token=3d91cde4a718ce18cd986a67eff38899f8eb1612ddae621507a00da28f4f)
[List of materials](btn:https://cryptpad.fr/sheet/#/2/sheet/view/SazDmGarJfd57LlUPitUPgCZ9pZgQzFs9noQ3YGG68w/embed/)
# Sensing
## electrical activity of a plant
......@@ -18,8 +25,6 @@ Depending on the first phase (which is sensing the electrical activity of a plan
# Interacting
---
# References
## Concept
......@@ -27,25 +32,16 @@ Depending on the first phase (which is sensing the electrical activity of a plan
- [Elowan (MIT Media Lab)](https://www.media.mit.edu/projects/elowan-a-plant-robot-hybrid/overview/)
is a cybernetic lifeform, a plant in direct dialogue with a machine. Using its own internal
electrical signals, the plant is interfaced with a robotic extension that drives it toward light.
[Plantoid](https://hackaday.io/project/134172-plantoid), an hybrid with actuators and electrical
potential sensing for plant control. [the Plant
SpykerBox](https://backyardbrains.com/products/plantspikerbox), a device that records and
visualizes the signals emitted by plants offering users the unique opportunity to peer into the
fascinating world of plant signaling and plant behaviors. [Cultivating
Frequencies](https://colinhonigman.com/Cultivating-Frequencies) transforms a garden into an
interactive and generative musical installation by analyzing data from the garden and translating
them into musical components. [The PhytoSense façade
system](http://www.iaacblog.com/programs/phytosense/) is an interactive green facade that uses
swept frequency capacitive sensing to play with light depending on touch. [Plant
Wave](https://www.plantwave.com/): Tune into nature and listen to the music of plants.
[SCÍON](https://www.instruomodular.com/product/scion/) is a biofeedback sensor built into a quad
random voltage generator. [Pulsum Plantae](http://lessnullvoid.cc/content/2011/10/pulsum-plantae/)
([Github project](https://github.com/Lessnullvoid/Pulsum-Plantae)) is a project focused on
bioelectrical activity readings from different types of plants. [Botanicus
Interacticus](http://www.ivanpoupyrev.com/project/botanicus-interacticus) allows for rich and
expressive interaction with plants. [Thesis “Sonnengarten – Urban Light Installation with
Human-Plant
Interaction”](https://sonnengartenjena.wordpress.com/2020/05/26/thesis-sonnengarten-urban-light-installation-with-human-plant-interaction/)
- [Plantoid](https://hackaday.io/project/134172-plantoid), an hybrid with actuators and electrical
potential sensing for plant control.
- [the Plant SpykerBox](https://backyardbrains.com/products/plantspikerbox), a device that records and visualizes the signals emitted by plants offering users the unique opportunity to peer into the fascinating world of plant signaling and plant behaviors.
- [Cultivating Frequencies](https://colinhonigman.com/Cultivating-Frequencies) transforms a garden into an interactive and generative musical installation by analyzing data from the garden and translating them into musical components.
- [The PhytoSense façade system](http://www.iaacblog.com/programs/phytosense/) is an interactive green facade that uses swept frequency capacitive sensing to play with light depending on touch.
- [Plant Wave](https://www.plantwave.com/): Tune into nature and listen to the music of plants.
- [SCÍON](https://www.instruomodular.com/product/scion/) is a biofeedback sensor built into a quad random voltage generator.
- [Pulsum Plantae](http://lessnullvoid.cc/content/2011/10/pulsum-plantae/) ([Github project](https://github.com/Lessnullvoid/Pulsum-Plantae)) is a project focused on bioelectrical activity readings from different types of plants.
- [Botanicus Interacticus](http://www.ivanpoupyrev.com/project/botanicus-interacticus) allows for rich and expressive interaction with plants.
- [Thesis “Sonnengarten – Urban Light Installation with Human-Plant Interaction”](https://sonnengartenjena.wordpress.com/2020/05/26/thesis-sonnengarten-urban-light-installation-with-human-plant-interaction/)
## Technique
- [FYI on DIYs in PHL: Data Garden](http://philadelphia.thedelimagazine.com/9336/fyi-on-diys-phl-data-garden): "The sensors are “psycho galvanometers” which graph changes in galvanic response (electrical “skin” conductance/resistance) by producing a square wave of variable frequency and pulse width. Built from a [555 timer IC](https://en.wikipedia.org/wiki/555_timer_IC) and a handful of electronic components, each sensor is attached to a leaf on a particular plant using an electrode of silver wire and conductive gel."
......
# dependencies
import os, shutil, warnings, os.path, time
import os
import shutil
import warnings
import os.path
import time
from datetime import datetime
from jinja2 import Environment, FileSystemLoader
from markdown2 import markdown
......@@ -25,16 +29,16 @@ warnings.filterwarnings("ignore", "(Possibly )?corrupt EXIF data", UserWarning)
# RSS
fg = FeedGenerator()
fg.title('antoine.studio')
fg.author( {'name':'Antoine Jaunard', 'email': '[email protected]'} )
fg.link( href=main_url, rel='alternate' )
fg.author({'name': 'Antoine Jaunard', 'email': '[email protected]'})
fg.link(href=main_url, rel='alternate')
fg.subtitle('experimental studio of Antoine Jaunard')
fg.language('en')
rssfeed = fg.rss_str(pretty=True)
rssfeed = fg.rss_str(pretty=True)
# Image utility
def image_util(old_file, new_file):
if not os.path.isfile(new_file):
# copy the original to public/media
# copy the original to public/media
shutil.copy2(old_file, new_file)
if new_file.endswith(tuple(media_ext)):
img = Image.open(new_file)
......@@ -44,6 +48,7 @@ def image_util(old_file, new_file):
img = img.resize((base_width, hsize), Image.ANTIALIAS)
img.save(new_file)
# purge public folder
for file_name in os.listdir(folder_html):
if file_name.endswith('.html'):
......@@ -69,14 +74,16 @@ for content_type in content_types:
for item in os.listdir(dir_path):
file_path = os.path.join(dir_path, item)
if file_path.endswith('.md'):
# markdown file
ARTICLE_DM[article_slug] = time.ctime(os.path.getmtime(file_path))
# markdown file
ARTICLE_DM[article_slug] = time.ctime(
os.path.getmtime(file_path))
with open(file_path, 'r') as file:
ARTICLES[article_slug] = markdown(
file.read(), extras=['metadata', 'tables', 'target-blank-links'])
file.read(), extras=['metadata', 'tables', 'target-blank-links', 'toc'])
else:
# media file
media_file_path = 'public/medias/{slug}'.format(slug=content_type + '-' + article_slug + '-' + item)
media_file_path = 'public/medias/{slug}'.format(
slug=content_type + '-' + article_slug + '-' + item)
image_util(file_path, media_file_path)
# reverse order
......@@ -86,8 +93,10 @@ for content_type in content_types:
for article in ARTICLES:
article_metadata = ARTICLES[article].metadata
article_date_created = datetime.strptime(ARTICLE_DC.get(article), '%Y-%m-%d').strftime('%d/%m/%Y')
article_date_modified = datetime.strptime(ARTICLE_DM.get(article), '%a %b %d %H:%M:%S %Y').strftime('%d/%m/%Y')
article_date_created = datetime.strptime(
ARTICLE_DC.get(article), '%Y-%m-%d').strftime('%d/%m/%Y')
article_date_modified = datetime.strptime(ARTICLE_DM.get(
article), '%a %b %d %H:%M:%S %Y').strftime('%d/%m/%Y')
if 'tags' in article_metadata:
tags = [article_metadata['tags']]
else:
......@@ -101,9 +110,10 @@ for content_type in content_types:
'content': ARTICLES[article],
'thumbnail': thumbnail,
'tags': tags,
'date_created' : article_date_created,
'date_modified' : article_date_modified,
'slug': content_type + '-' + article
'date_created': article_date_created,
'date_modified': article_date_modified,
'slug': content_type + '-' + article,
'toc': ARTICLES[article].toc_html
}
ARTICLES[article].metadata = article_data
article_html = article_template.render(article=article_data)
......@@ -111,12 +121,16 @@ for content_type in content_types:
slug=article_data['slug'])
os.makedirs(os.path.dirname(article_file_path), exist_ok=True)
img_tag = '<img src ="medias/' + article_data['slug'] + '-'
video_tag = '<video controls preload="auto"><source type ="video/mp4" src ="medias/' + article_data['slug'] + '-'
doc_link = '<a target="_blank" href="medias/' + article_data['slug'] + '-'
video_tag = '<video controls preload="auto"><source type ="video/mp4" src ="medias/' + \
article_data['slug'] + '-'
doc_link = '<a target="_blank" href="medias/' + \
article_data['slug'] + '-'
article_html = article_html.replace('<img src="', img_tag)
article_html = article_html.replace('<video><source src="', video_tag)
article_html = article_html.replace('<a target="_blank" href="files/', doc_link)
article_html = article_html.replace('<p>TODO:', '<p class="todo">TODO:')
article_html = article_html.replace(
'<a target="_blank" href="files/', doc_link)
article_html = article_html.replace(
'<p>TODO:', '<p class="todo">TODO:')
article_html = article_html.replace('href="btn:', 'class="btn" href="')
with open(article_file_path, 'w') as file:
file.write(article_html)
......@@ -125,8 +139,10 @@ for content_type in content_types:
fe = fg.add_entry()
fe.title(article_data['title'])
fe.link(href=main_url + article_data['slug'] + '.html')
fe.author( {'name':'Antoine Jaunard', 'email': '[email protected]'} )
fe.pubDate(datetime.strptime(ARTICLE_DC.get(article), '%Y-%m-%d').strftime('%a %b %d %H:%M:%S %Y') + ' +0200')
fe.author({'name': 'Antoine Jaunard',
'email': '[email protected]'})
fe.pubDate(datetime.strptime(ARTICLE_DC.get(article),
'%Y-%m-%d').strftime('%a %b %d %H:%M:%S %Y') + ' +0200')
fe.description(article_data['content'][:800] + '...')
METADATA[content_metadata_name] = [
......@@ -154,7 +170,7 @@ FILES = {
FLUX['flux_metadata'] = [
FILES[file] for file in FILES
]
flux_html = flux_template.render(flux = FLUX['flux_metadata'])
flux_html = flux_template.render(flux=FLUX['flux_metadata'])
flux_html = flux_html.replace('<img src="public/', '<img src="')
with open('public/flux.html', 'w') as file:
file.write(flux_html)
......@@ -186,5 +202,6 @@ for file_name in os.listdir(folder_html):
if file_name.endswith('.html'):
total_page = total_page + 1
for file_name in os.listdir(folder_medias):
total_media = total_media + 1
print('The website consists of', total_page, 'pages and', total_media, 'medias')
total_media = total_media + 1
print('The website consists of', total_page,
'pages and', total_media, 'medias')
/* reset */
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline;}
.nav__list, ul.index__list {list-style: none;}
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
.nav__list, ul.index__list {
list-style: none;
}
/* variables */
:root {
--color-base: navy;
--color-body: white;
......@@ -17,6 +29,7 @@ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockq
--mobile: 600px;
--border-sm: 1px;
}
@media (max-width: 500px) {
:root {
--spacing: 15px;
......@@ -25,75 +38,255 @@ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockq
}
/* base */
html {font: var(--font-size-base)/1.7 var(--font-family)}
body {padding: var(--spacing); color: var(--color-base); background-color: var(--color-body);}
a {color: var(--color-action); text-decoration: none;}
img {max-width: 100%;}
html {
font: var(--font-size-base)/1.7 var(--font-family)
}
body {
padding: var(--spacing);
color: var(--color-base);
background-color: var(--color-body);
}
a {
color: var(--color-action);
text-decoration: none;
}
img {
max-width: 100%;
}
/* header */
.header {position: sticky; top: var(--spacing-sm); z-index: 2; display: flex; justify-content: space-between; margin-bottom: calc(var(--spacing-sm));}
.header a {background-color: var(--color-body); padding: 0.3em 0;}
.header .title a {color: var(--color-base);}
.header .title a span {color: var(--color-action);}
.nav li {padding: 0;}
.nav li:before {content: none;}
.header {
display: flex;
justify-content: space-between;
margin-bottom: calc(var(--spacing-sm));
}
.header a {
background-color: var(--color-body);
padding: 0.3em 0;
}
.header .title a {
color: var(--color-base);
}
.header .title a span {
color: var(--color-action);
}
.nav li {
padding: 0;
}
.nav li:before {
content: none;
}
/* index */
.index {margin-bottom: var(--spacing);}
.index {
margin-bottom: var(--spacing);
}
/* .index:last-of-type {margin-bottom: calc(var(--spacing)*2);} */
/* .index__title {display: table; margin: 1.8em 0 0.8em 0; color: var(--color-base);border-bottom: var(--border-sm) solid var(--color-base); margin: 2.4em 0 1.8em 0;} */
/* flux */
.flux {display: grid; align-items: center; grid-gap: var(--spacing); grid-template-columns: repeat(3, 1fr);}
@media (max-width: 500px) {.flux {grid-template-columns: 1fr;}}
.flux__item, .flux__img { display: block; width: 100%;}
.flux__desc {margin-top: calc(var(--spacing)/5);}
.flux {
display: grid;
align-items: center;
grid-gap: var(--spacing);
grid-template-columns: repeat(3, 1fr);
}
@media (max-width: 500px) {
.flux {
grid-template-columns: 1fr;
}
}
.flux__item, .flux__img {
display: block;
width: 100%;
}
.flux__desc {
margin-top: calc(var(--spacing)/5);
}
/* article */
.article { max-width: 760px;}
.article h1,
.article h2,
.article h3,
.article h4,
.article h5,
.article h6 {
display: table; margin: 1.8em 0 0.8em 0; color: var(--color-heading-low);
}
.article h1, .article h2 {color: var(--color-heading-high);}
.article h1 {border-bottom: var(--border-sm) solid var(--color-heading-high); margin: 2.4em 0 1.8em 0;}
h1.article__title {margin-top: 0;}
.article p {margin-bottom: 0.6em;}
.article img {display: block;}
.article a {text-decoration: underline;}
ol {list-style: decimal-leading-zero; margin-left: 2.4em;}
ul {list-style: none;}
ul li {position: relative; padding-left: 1em;}
ul li:before { content: "-"; position: absolute; top: 0; left: 0; color: var(--color-heading-high);}
ol li::marker {color: var(--color-heading-high);}
.article blockquote { border-left: var(--border-sm) var(--color-heading-high) dotted; padding-left: var(--spacing); margin: 1.8em 0;}
.article blockquote blockquote {border: none; padding: 0; margin: 0;}
.article blockquote blockquote p {font-style: italic;}
.article pre, .article code {background-color: var(--color-base); color: var(--color-body);}
.article pre {display: block; padding: 1.2em; overflow-x: auto;}
.article code {display: inline; padding: 0.15em 0.2em; border: var(--border-sm) solid var(--color-body);}
.article hr {display: block; border: none; margin: 2.4em 0; height: var(--border-sm); background-color: var(--color-base);}
.article em {font-style: italic;}
.article strong {font-weight: bold;}
.article table {width: 100%;}
.article th,
.article td {text-align: left; padding: calc(var(--spacing)/4);}
.article th {border-bottom: var(--border-sm) solid var(--color-base);}
.article table tr:nth-child(even) {background-color: var(--color-light);}
.article video {display: block; width: 100%;}
.article img, .article video, .article table, .article ul,.article ol, .embed-container, .article pre { margin-top: 1.4em; margin-bottom: 1.4em;}
.article img {font-size: 0.8em; text-transform: uppercase; font-style: italic;}
p.todo {font-size: 0.8em; text-transform: uppercase; border: var(--border-sm) var(--color-base) dotted; padding: var(--spacing-sm); opacity: 0.5;}
.article {
max-width: 760px;
}
.article h1, .article h2, .article h3, .article h4, .article h5, .article h6 {
display: table;
margin: 1.8em 0 0.8em 0;
color: var(--color-heading-low);
}
.article h1, .article h2 {
color: var(--color-heading-high);
}
.article h1 {
border-bottom: var(--border-sm) solid var(--color-heading-high);
margin: 2.4em 0 1.8em 0;
}
h1.article__title {
margin-top: 0;
}
.article p {
margin-bottom: 0.6em;
}
.article img {
display: block;
}
.article a {
text-decoration: underline;
}
ol {
list-style: decimal-leading-zero;
margin-left: 2.4em;
}
ul {
list-style: none;
}
ul li {
position: relative;
padding-left: 1em;
}
ul li:before {
content: "-";
position: absolute;
top: 0;
left: 0;
color: var(--color-heading-high);
}
ol li::marker {
color: var(--color-heading-high);
}
.article blockquote {
border-left: var(--border-sm) var(--color-heading-high) dotted;
padding-left: var(--spacing);
margin: 1.8em 0;
}
.article blockquote blockquote {
border: none;
padding: 0;
margin: 0;
}
.article blockquote blockquote p {
font-style: italic;
}
.article pre, .article code {
background-color: var(--color-base);
color: var(--color-body);
}
.article pre {
display: block;
padding: 1.2em;
overflow-x: auto;
}
.article code {
display: inline;
padding: 0.15em 0.2em;
border: var(--border-sm) solid var(--color-body);
}
.article hr {
display: block;
border: none;
margin: 2.4em 0;
height: var(--border-sm);
background-color: var(--color-base);
}
.article em {
font-style: italic;
}
.article strong {
font-weight: bold;
}
.article table {
width: 100%;
}
.article th, .article td {
text-align: left;
padding: calc(var(--spacing)/4);
}
.article th {
border-bottom: var(--border-sm) solid var(--color-base);
}
.article table tr:nth-child(even) {
background-color: var(--color-light);
}
.article video {
display: block;
width: 100%;
}
.article img, .article video, .article table, .article ul, .article ol, .embed-container, .article pre {
margin-top: 1.4em;
margin-bottom: 1.4em;
}
.article img {
font-size: 0.8em;
text-transform: uppercase;
font-style: italic;
}
p.todo {
font-size: 0.8em;
text-transform: uppercase;
border: var(--border-sm) var(--color-base) dotted;
padding: var(--spacing-sm);
opacity: 0.5;
}
.article footer {
margin-top: calc(var(--spacing));
margin-bottom: calc(var(--spacing-sm));
}
.article footer p {margin-bottom: 0;}
.article footer span {color: var(--color-heading-high);}
.article footer p {
margin-bottom: 0;
}
.article footer span {
color: var(--color-heading-high);
}
.article .btn {
display: inline-block;
font-size: 0.8em;
......@@ -103,8 +296,41 @@ p.todo {font-size: 0.8em; text-transform: uppercase; border: var(--border-sm) va
padding: calc(var(--spacing-sm) * 2);
color: var(--color-body);
}
.toc ul {
margin: 0;
border-left: var(--border-sm) solid var(--color-heading-high);
}
.toc li:before {
content: none;
}
.toc a {
color: var(--color-base);
}
/* footer */
.footer .sep {display: block; margin-bottom: var(--spacing-sm);}
.footer .sep {
display: block;
margin-bottom: var(--spacing-sm);
}
/* embed */
.embed-container { position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden; max-width: 100%; } .embed-container iframe, .embed-container object, .embed-container embed { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
.embed-container {
position: relative;
padding-bottom: 56.25%;
height: 0;
overflow: hidden;
max-width: 100%;
}
.embed-container iframe, .embed-container object, .embed-container embed {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
\ No newline at end of file
......@@ -30,9 +30,10 @@
<h1 class="article__title">
3D scanning and printing
</h1>
<p>3D printing is not as simple as pressing the <em>print</em> button and waiting for the model to be printed. It is crucial to know how to prepare a 3D model, how the printer works and what are its limits, and how to balance printing time with print quality. This week, I'm going to test a printer, scan and print something.</p>
<h1>Printing a 3D test file</h1>
<h1 id="printing-a-3d-test-file">Printing a 3D test file</h1>
<p>For this week's group assignement, I teamed up with <a target="_blank" href="https://fabacademy.org/2020/labs/barcelona/students/tue-ngo/">Tue</a> and <a target="_blank" href="https://fabacademy.org/2020/labs/barcelona/students/david-prieto/">David</a>. We picked a <a target="_blank" href="https://www.thingiverse.com/thing:1363023">test file</a> from Thingiverse in order to test different features of one of the 3D printers we have in Fab Lab Barcelona, the <a target="_blank" href="https://www.creality3d.shop/products/creality-cr-10s-s5-3d-printer-diy-kit-large-printing-size-500x500x500mm">Creality3D CR-10 S5 3D</a>.</p>
......@@ -50,7 +51,7 @@
<li>2 different extrusion widths: 0.48mm &amp; 0.4mm</li>
</ul>
<h2>Slicing</h2>
<h2 id="slicing">Slicing</h2>
<p>To be able to print a 3D model, we have to send instructions to the printer, wich are written in <a target="_blank" href="https://en.wikipedia.org/wiki/G-code">g-code</a> and tells the motors where to move.
To prepare the g-code, we have to <em>slice</em> our 3D model (.STL), to simulate and anticipate how the model will be printed, according to the printer settings and gravity law.</p>
......@@ -59,7 +60,7 @@ To prepare the g-code, we have to <em>slice</em> our 3D model (.STL), to simulat
<p>At Fab Lab Barcelona, a computer with <a target="_blank" href="https://ultimaker.com/software/ultimaker-cura">Ultimaker Cura</a> is attached to the machines, with all the presets of the differents printers saved in it. It's therefore easier to directly use it in order to slice our model instead of searching the presets and install them on our personal laptop.</p>
<h3>Specific settings that we had to specify</h3>
<h3 id="specific-settings-that-we-had-to-specify">Specific settings that we had to specify</h3>
<ul>
<li>layer height: <code>0.25mm</code></li>
......@@ -68,7 +69,7 @@ To prepare the g-code, we have to <em>slice</em> our 3D model (.STL), to simulat
<li>Print speed: <code>60mm/s</code> (= maximum for this printer)</li>
</ul>
<h2>Printing</h2>
<h2 id="printing">Printing</h2>
<p>The filament we use is a <code>PLA 1.75mm</code>. It's a plant-based material made from starches like soybeans or corn. It needs to be heated at 190-200C° to be used.</p>
......@@ -81,13 +82,13 @@ To prepare the g-code, we have to <em>slice</em> our 3D model (.STL), to simulat
<p><img src ="medias/fabac-assignments-3D-scanning-printing-testprint.JPG" alt="testprint" />