Skip to content

Commit 547ef26

Browse files
committed
Vendor a glyphicons-only stylesheet instead of the full Bootstrap 3 base
folium loaded netdna's bootstrap-glyphicons.css for the Awesome Markers glyphicon font. Despite its name that file is a full Bootstrap 3 base stylesheet: alongside the @font-face/.glyphicon rules it ships a normalize reset and global body styles (font, colour, background, margin). Loaded after Bootstrap 5, those body rules leaked onto the host page and, e.g., mispositioned user-added Bootstrap 5 components (GH-1820). Ship a vendored folium/templates/glyphicons.css with only the @font-face and .glyphicon/.glyphicon-* rules (fonts served from jsDelivr), referenced the same way as leaflet.awesome.rotate.css. Icon(prefix="glyphicon") markers keep working; the page-wide body/reset pollution is gone. Closes #1820
1 parent 5a41006 commit 547ef26

3 files changed

Lines changed: 226 additions & 2 deletions

File tree

folium/folium.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,13 @@
4343
"bootstrap_css",
4444
"https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css",
4545
),
46-
# glyphicons came from Bootstrap 3 and are used for Awesome Markers
46+
# glyphicons came from Bootstrap 3 and are used for Awesome Markers.
47+
# This vendored file has only the @font-face/.glyphicon rules; the previous
48+
# netdna stylesheet also shipped a full Bootstrap-3 reset that leaked global
49+
# ``body`` styles onto the host page (GH-1820).
4750
(
4851
"glyphicons_css",
49-
"https://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-glyphicons.css",
52+
"https://cdn.jsdelivr.net/gh/python-visualization/folium/folium/templates/glyphicons.css",
5053
),
5154
(
5255
"awesome_markers_font_css",

folium/templates/glyphicons.css

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
/*
2+
* Glyphicons Halflings -- the icon font used by folium's default Awesome Markers
3+
* (Icon(prefix="glyphicon")). Extracted from Bootstrap 3's glyphicon rules only.
4+
*
5+
* folium previously loaded netdna's bootstrap-glyphicons.css, which despite its
6+
* name ships a full Bootstrap-3 base stylesheet (normalize reset + global `body`
7+
* rules) that leaked onto the host page (GH-1820). This file provides just the
8+
* @font-face and .glyphicon/.glyphicon-* rules needed for the markers, with no
9+
* page-wide side effects. Font files are served from jsDelivr.
10+
*/
11+
@font-face{font-family:'Glyphicons Halflings';src:url('https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/fonts/glyphicons-halflings-regular.eot');src:url('https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),url('https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/fonts/glyphicons-halflings-regular.woff') format('woff'),url('https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/fonts/glyphicons-halflings-regular.ttf') format('truetype'),url('https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/fonts/glyphicons-halflings-regular.svg#glyphicons-halflingsregular') format('svg');}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;}
12+
.glyphicon-asterisk:before{content:"\2a";}
13+
.glyphicon-plus:before{content:"\2b";}
14+
.glyphicon-euro:before{content:"\20ac";}
15+
.glyphicon-minus:before{content:"\2212";}
16+
.glyphicon-cloud:before{content:"\2601";}
17+
.glyphicon-envelope:before{content:"\2709";}
18+
.glyphicon-pencil:before{content:"\270f";}
19+
.glyphicon-glass:before{content:"\e001";}
20+
.glyphicon-music:before{content:"\e002";}
21+
.glyphicon-search:before{content:"\e003";}
22+
.glyphicon-heart:before{content:"\e005";}
23+
.glyphicon-star:before{content:"\e006";}
24+
.glyphicon-star-empty:before{content:"\e007";}
25+
.glyphicon-user:before{content:"\e008";}
26+
.glyphicon-film:before{content:"\e009";}
27+
.glyphicon-th-large:before{content:"\e010";}
28+
.glyphicon-th:before{content:"\e011";}
29+
.glyphicon-th-list:before{content:"\e012";}
30+
.glyphicon-ok:before{content:"\e013";}
31+
.glyphicon-remove:before{content:"\e014";}
32+
.glyphicon-zoom-in:before{content:"\e015";}
33+
.glyphicon-zoom-out:before{content:"\e016";}
34+
.glyphicon-off:before{content:"\e017";}
35+
.glyphicon-signal:before{content:"\e018";}
36+
.glyphicon-cog:before{content:"\e019";}
37+
.glyphicon-trash:before{content:"\e020";}
38+
.glyphicon-home:before{content:"\e021";}
39+
.glyphicon-file:before{content:"\e022";}
40+
.glyphicon-time:before{content:"\e023";}
41+
.glyphicon-road:before{content:"\e024";}
42+
.glyphicon-download-alt:before{content:"\e025";}
43+
.glyphicon-download:before{content:"\e026";}
44+
.glyphicon-upload:before{content:"\e027";}
45+
.glyphicon-inbox:before{content:"\e028";}
46+
.glyphicon-play-circle:before{content:"\e029";}
47+
.glyphicon-repeat:before{content:"\e030";}
48+
.glyphicon-refresh:before{content:"\e031";}
49+
.glyphicon-list-alt:before{content:"\e032";}
50+
.glyphicon-flag:before{content:"\e034";}
51+
.glyphicon-headphones:before{content:"\e035";}
52+
.glyphicon-volume-off:before{content:"\e036";}
53+
.glyphicon-volume-down:before{content:"\e037";}
54+
.glyphicon-volume-up:before{content:"\e038";}
55+
.glyphicon-qrcode:before{content:"\e039";}
56+
.glyphicon-barcode:before{content:"\e040";}
57+
.glyphicon-tag:before{content:"\e041";}
58+
.glyphicon-tags:before{content:"\e042";}
59+
.glyphicon-book:before{content:"\e043";}
60+
.glyphicon-print:before{content:"\e045";}
61+
.glyphicon-font:before{content:"\e047";}
62+
.glyphicon-bold:before{content:"\e048";}
63+
.glyphicon-italic:before{content:"\e049";}
64+
.glyphicon-text-height:before{content:"\e050";}
65+
.glyphicon-text-width:before{content:"\e051";}
66+
.glyphicon-align-left:before{content:"\e052";}
67+
.glyphicon-align-center:before{content:"\e053";}
68+
.glyphicon-align-right:before{content:"\e054";}
69+
.glyphicon-align-justify:before{content:"\e055";}
70+
.glyphicon-list:before{content:"\e056";}
71+
.glyphicon-indent-left:before{content:"\e057";}
72+
.glyphicon-indent-right:before{content:"\e058";}
73+
.glyphicon-facetime-video:before{content:"\e059";}
74+
.glyphicon-picture:before{content:"\e060";}
75+
.glyphicon-map-marker:before{content:"\e062";}
76+
.glyphicon-adjust:before{content:"\e063";}
77+
.glyphicon-tint:before{content:"\e064";}
78+
.glyphicon-edit:before{content:"\e065";}
79+
.glyphicon-share:before{content:"\e066";}
80+
.glyphicon-check:before{content:"\e067";}
81+
.glyphicon-move:before{content:"\e068";}
82+
.glyphicon-step-backward:before{content:"\e069";}
83+
.glyphicon-fast-backward:before{content:"\e070";}
84+
.glyphicon-backward:before{content:"\e071";}
85+
.glyphicon-play:before{content:"\e072";}
86+
.glyphicon-pause:before{content:"\e073";}
87+
.glyphicon-stop:before{content:"\e074";}
88+
.glyphicon-forward:before{content:"\e075";}
89+
.glyphicon-fast-forward:before{content:"\e076";}
90+
.glyphicon-step-forward:before{content:"\e077";}
91+
.glyphicon-eject:before{content:"\e078";}
92+
.glyphicon-chevron-left:before{content:"\e079";}
93+
.glyphicon-chevron-right:before{content:"\e080";}
94+
.glyphicon-plus-sign:before{content:"\e081";}
95+
.glyphicon-minus-sign:before{content:"\e082";}
96+
.glyphicon-remove-sign:before{content:"\e083";}
97+
.glyphicon-ok-sign:before{content:"\e084";}
98+
.glyphicon-question-sign:before{content:"\e085";}
99+
.glyphicon-info-sign:before{content:"\e086";}
100+
.glyphicon-screenshot:before{content:"\e087";}
101+
.glyphicon-remove-circle:before{content:"\e088";}
102+
.glyphicon-ok-circle:before{content:"\e089";}
103+
.glyphicon-ban-circle:before{content:"\e090";}
104+
.glyphicon-arrow-left:before{content:"\e091";}
105+
.glyphicon-arrow-right:before{content:"\e092";}
106+
.glyphicon-arrow-up:before{content:"\e093";}
107+
.glyphicon-arrow-down:before{content:"\e094";}
108+
.glyphicon-share-alt:before{content:"\e095";}
109+
.glyphicon-resize-full:before{content:"\e096";}
110+
.glyphicon-resize-small:before{content:"\e097";}
111+
.glyphicon-exclamation-sign:before{content:"\e101";}
112+
.glyphicon-gift:before{content:"\e102";}
113+
.glyphicon-leaf:before{content:"\e103";}
114+
.glyphicon-eye-open:before{content:"\e105";}
115+
.glyphicon-eye-close:before{content:"\e106";}
116+
.glyphicon-warning-sign:before{content:"\e107";}
117+
.glyphicon-plane:before{content:"\e108";}
118+
.glyphicon-random:before{content:"\e110";}
119+
.glyphicon-comment:before{content:"\e111";}
120+
.glyphicon-magnet:before{content:"\e112";}
121+
.glyphicon-chevron-up:before{content:"\e113";}
122+
.glyphicon-chevron-down:before{content:"\e114";}
123+
.glyphicon-retweet:before{content:"\e115";}
124+
.glyphicon-shopping-cart:before{content:"\e116";}
125+
.glyphicon-folder-close:before{content:"\e117";}
126+
.glyphicon-folder-open:before{content:"\e118";}
127+
.glyphicon-resize-vertical:before{content:"\e119";}
128+
.glyphicon-resize-horizontal:before{content:"\e120";}
129+
.glyphicon-hdd:before{content:"\e121";}
130+
.glyphicon-bullhorn:before{content:"\e122";}
131+
.glyphicon-certificate:before{content:"\e124";}
132+
.glyphicon-thumbs-up:before{content:"\e125";}
133+
.glyphicon-thumbs-down:before{content:"\e126";}
134+
.glyphicon-hand-right:before{content:"\e127";}
135+
.glyphicon-hand-left:before{content:"\e128";}
136+
.glyphicon-hand-up:before{content:"\e129";}
137+
.glyphicon-hand-down:before{content:"\e130";}
138+
.glyphicon-circle-arrow-right:before{content:"\e131";}
139+
.glyphicon-circle-arrow-left:before{content:"\e132";}
140+
.glyphicon-circle-arrow-up:before{content:"\e133";}
141+
.glyphicon-circle-arrow-down:before{content:"\e134";}
142+
.glyphicon-globe:before{content:"\e135";}
143+
.glyphicon-tasks:before{content:"\e137";}
144+
.glyphicon-filter:before{content:"\e138";}
145+
.glyphicon-fullscreen:before{content:"\e140";}
146+
.glyphicon-dashboard:before{content:"\e141";}
147+
.glyphicon-heart-empty:before{content:"\e143";}
148+
.glyphicon-link:before{content:"\e144";}
149+
.glyphicon-phone:before{content:"\e145";}
150+
.glyphicon-usd:before{content:"\e148";}
151+
.glyphicon-gbp:before{content:"\e149";}
152+
.glyphicon-sort:before{content:"\e150";}
153+
.glyphicon-sort-by-alphabet:before{content:"\e151";}
154+
.glyphicon-sort-by-alphabet-alt:before{content:"\e152";}
155+
.glyphicon-sort-by-order:before{content:"\e153";}
156+
.glyphicon-sort-by-order-alt:before{content:"\e154";}
157+
.glyphicon-sort-by-attributes:before{content:"\e155";}
158+
.glyphicon-sort-by-attributes-alt:before{content:"\e156";}
159+
.glyphicon-unchecked:before{content:"\e157";}
160+
.glyphicon-expand:before{content:"\e158";}
161+
.glyphicon-collapse-down:before{content:"\e159";}
162+
.glyphicon-collapse-up:before{content:"\e160";}
163+
.glyphicon-log-in:before{content:"\e161";}
164+
.glyphicon-flash:before{content:"\e162";}
165+
.glyphicon-log-out:before{content:"\e163";}
166+
.glyphicon-new-window:before{content:"\e164";}
167+
.glyphicon-record:before{content:"\e165";}
168+
.glyphicon-save:before{content:"\e166";}
169+
.glyphicon-open:before{content:"\e167";}
170+
.glyphicon-saved:before{content:"\e168";}
171+
.glyphicon-import:before{content:"\e169";}
172+
.glyphicon-export:before{content:"\e170";}
173+
.glyphicon-send:before{content:"\e171";}
174+
.glyphicon-floppy-disk:before{content:"\e172";}
175+
.glyphicon-floppy-saved:before{content:"\e173";}
176+
.glyphicon-floppy-remove:before{content:"\e174";}
177+
.glyphicon-floppy-save:before{content:"\e175";}
178+
.glyphicon-floppy-open:before{content:"\e176";}
179+
.glyphicon-credit-card:before{content:"\e177";}
180+
.glyphicon-transfer:before{content:"\e178";}
181+
.glyphicon-cutlery:before{content:"\e179";}
182+
.glyphicon-header:before{content:"\e180";}
183+
.glyphicon-compressed:before{content:"\e181";}
184+
.glyphicon-earphone:before{content:"\e182";}
185+
.glyphicon-phone-alt:before{content:"\e183";}
186+
.glyphicon-tower:before{content:"\e184";}
187+
.glyphicon-stats:before{content:"\e185";}
188+
.glyphicon-sd-video:before{content:"\e186";}
189+
.glyphicon-hd-video:before{content:"\e187";}
190+
.glyphicon-subtitles:before{content:"\e188";}
191+
.glyphicon-sound-stereo:before{content:"\e189";}
192+
.glyphicon-sound-dolby:before{content:"\e190";}
193+
.glyphicon-sound-5-1:before{content:"\e191";}
194+
.glyphicon-sound-6-1:before{content:"\e192";}
195+
.glyphicon-sound-7-1:before{content:"\e193";}
196+
.glyphicon-copyright-mark:before{content:"\e194";}
197+
.glyphicon-registration-mark:before{content:"\e195";}
198+
.glyphicon-cloud-download:before{content:"\e197";}
199+
.glyphicon-cloud-upload:before{content:"\e198";}
200+
.glyphicon-tree-conifer:before{content:"\e199";}
201+
.glyphicon-tree-deciduous:before{content:"\e200";}
202+
.glyphicon-briefcase:before{content:"\1f4bc";}
203+
.glyphicon-calendar:before{content:"\1f4c5";}
204+
.glyphicon-pushpin:before{content:"\1f4cc";}
205+
.glyphicon-paperclip:before{content:"\1f4ce";}
206+
.glyphicon-camera:before{content:"\1f4f7";}
207+
.glyphicon-lock:before{content:"\1f512";}
208+
.glyphicon-bell:before{content:"\1f514";}
209+
.glyphicon-bookmark:before{content:"\1f516";}
210+
.glyphicon-fire:before{content:"\1f525";}
211+
.glyphicon-wrench:before{content:"\1f527";}

tests/test_folium.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,16 @@ def test_location_args():
5353
assert m.location == [45.5236, -122.6750]
5454

5555

56+
def test_glyphicons_css_is_vendored():
57+
# GH-1820: the glyphicon stylesheet must be folium's vendored glyphicons-only
58+
# file, not netdna's ``bootstrap-glyphicons.css`` which despite its name ships
59+
# a full Bootstrap-3 reset that leaked global ``body`` styles onto the page.
60+
m = folium.Map()
61+
html = m.get_root().render()
62+
assert "folium/templates/glyphicons.css" in html
63+
assert "netdna.bootstrapcdn.com" not in html
64+
65+
5666
class TestFolium:
5767
"""Test class for the Folium library."""
5868

0 commit comments

Comments
 (0)