-
Notifications
You must be signed in to change notification settings - Fork 0
/
css-generate.py
executable file
·2028 lines (1858 loc) · 163 KB
/
css-generate.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/env python
"Accessibility CSS Generator, (c) Silas S. Brown 2006-24. Version 0.9938"
# Works on either Python 2 or Python 3
# Website: http://ssb22.user.srcf.net/css/
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# CHANGES
# -------
# If you want to compare this code to old versions, the old
# versions are being kept in the E-GuideDog SVN repository on
# http://svn.code.sf.net/p/e-guidedog/code/ssb22/css-generator
# and on GitHub at https://github.com/ssb22/css-generator
# and on GitLab at https://gitlab.com/ssb22/css-generator
# and on BitBucket https://bitbucket.org/ssb22/css-generator
# and at https://gitlab.developers.cam.ac.uk/ssb22/css-generator
# and in China: https://gitee.com/ssb22/css-generator
# as of v0.9782. Versions prior to that were not kept, but
# some were captured by Internet Archive at
# https://web.archive.org/web/*/http://people.pwf.cam.ac.uk/ssb22/css/css-generate.py
# CONFIGURATION
# -------------
# The default configuration can be changed here, or if you
# prefer you can copy and paste all of this to a separate
# file called css_generate_config.py and edit it there
# (which should make it easier to update the code
# independently of your configuration).
# Where to put the output CSS files: default is current directory,
# but you might want to put them in a subdirectory, or set
# an absolute path
outputDir = "."
try: True # backward compatibility with Python 2.1:
except: exec("True,False=1,0") # (syntax error in Python 3)
# Whether to write an HTML menu to standard output as well
# (see my CSS page for example)
outHTML = True
# if putting this on the Web, include in .htaccess:
# AddType text/css css
# SetEnvIf Request_URI "\.css$" requested_css=css
# Header add Content-Disposition "Attachment" env=requested_css
# Which pixel sizes to generate.
# Size 0 means "unchanged" - it will disable the size
# changes, and the layout changes that are meant for large
# sizes. This is for people who need only colour changes,
# or for whom the browser's built-in zoom is sufficient
# (e.g. on a large display and/or complex layouts that
# aren't very-well dealt with by our layout changes).
pixel_sizes_to_generate = [0,18,20,25,30,35,40,45,50,60,75,100]
# Which pixel size to use with the "chop" and related debug options:
chop_pixel_size = 20
# Which colour schemes to generate. The format of this is
# [("description","filename-prefix",{...}),...]
# - see the existing ones for examples.
# If you have many of these, you might want to read them
# in from separate configuration files instead of listing
# them here. In that case, set colour_schemes_to_generate
# to a string containing a wildcard,
# e.g. colour_schemes_to_generate = "my_config_dir/*.conf"
# in which case each file in my_config_dir/*.conf should
# contain one or more entries in the same [(...),...] format
# WARNING: this will be passed to Python's eval() function
# which can execute any command, so don't allow an
# untrusted third party to write these *.conf files.
colour_schemes_to_generate = [
("yellow on black","",
{"text":"yellow","background":"black",
"translucent_background_compromise":"rgba(0,0,0,0.5)",
"headings":"#8080FF","link":"#00FF00",
"hover-bkg":"#0000C0","visited":"#00FFFF",
"bold":"#FFFF80","italic":"white",
"coloured":"pink", # used for text inside any FONT COLOR= markup
"button-bkg":"#600040",
"select-bkg":"#600060",
"reset-bkg":"#400060",
"form_disabled":"#404040", # GrayText requires CSS 2.1
"selection-bkg":"#006080", # (if supported by the browser. BEWARE: Some browsers, e.g. Safari 6, will NOT display this exact colour, but a computed medium mid-way between it and the unselected background; you should therefore ensure that other backgrounds (e.g. highlight) are discernable against those 'computed medium' colours as well)
"highlight-bkg":"#300030", # (misc non-selection highlights in site-specific hacks)
"image_transparency_compromise":"#808000" # non-black and non-white background for transparent images, so at least stand a chance of seeing transparent imgs that were meant for white bkg (or black bkg)
}),
("green on black","green",
{"text":"#00FF00","background":"black",
"translucent_background_compromise":"rgba(0,0,0,0.5)",
"headings":"#40C080","link":"#008AFF",
"hover-bkg":"#400000","visited":"#00FFFF",
"bold":"#80FF80","italic":"white",
"button-bkg":"#600040",
"select-bkg":"#600060",
"coloured":"#80C040",
"reset-bkg":"#400060",
"form_disabled":"#404040",
"selection-bkg":"#4000c0",
"highlight-bkg":"#003050",
"image_transparency_compromise":"#808000"
}),
("white on black","WonB",
{"text":"white","background":"black",
"translucent_background_compromise":"rgba(0,0,0,0.5)",
"headings":"#40C090","link":"#0080FF",
"hover-bkg":"#400000","visited":"#00FFFF",
"bold":"yellow","italic":"#FFFF80",
"button-bkg":"#600040",
"select-bkg":"#600060",
"coloured":"#FFFF40",
"reset-bkg":"#400060",
"form_disabled":"#404040",
"selection-bkg":"#4080c0",
"highlight-bkg":"#003050",
"image_transparency_compromise":"#808080"
}),
("soft greys","soft", # c.f. Nightshift etc; thanks to Liviu Andronic for testing
{"text":"#C0C0C0","background":"#383838",
"translucent_background_compromise":"rgba(56,56,56,0.5)",
"alt-backgrounds":["#333333","#2E2E2E"], # optional
"headings":"#40C090","link":"#BDB76B",
"hover-bkg":"#453436","visited":"#B6AA7B",
"bold":"#CD853F","italic":"#EFEFCF",
"button-bkg":"#553030",
"select-bkg":"#603440",
"coloured":"#E0E040",
"reset-bkg":"#303055",
"form_disabled":"#555753",
"selection-bkg":"#5f5f5f",
"highlight-bkg":"#2e3050",
"focusOutlineStyle":"solid #006080",
"image_opacity":0.8,
"image_transparency_compromise":"#2e3436"
}),
("black on linen","BonL", # LyX's background colour is "linen", 240/230/220
{"text":"black","background":"#faf0e6",
"translucent_background_compromise":"rgba(250,240,230,0.5)",
"headings":"#404040","link":"#0000FF",
"hover-bkg":"#80C0C0","visited":"#008020",
"bold":"black","italic":"#400000",
"button-bkg":"#608040",
"select-bkg":"#608060",
"coloured":"#001040",
"reset-bkg":"#408060",
"form_disabled":"#A0A0A0",
"highlight-bkg":"#FFFFE6",
"image_transparency_compromise":"#faf0e6"
}),
("black on white","BonW", # cld call this "black on bright white" (as opposed to "black on linen white") but that causes the list to take up more width
{"text":"black","background":"white",
"translucent_background_compromise":"rgba(255,255,255,0.5)",
"headings":"#404040","link":"#0000FF",
"hover-bkg":"#80C0C0","visited":"#008020",
"bold":"black","italic":"#400000",
"button-bkg":"#608040",
"select-bkg":"#608060",
"coloured":"#001040",
"reset-bkg":"#408060",
"form_disabled":"#A0A0A0",
"highlight-bkg":"#FFFF80",
"image_transparency_compromise":"white"
}),
]
# Some other options you might want to change:
separate_adjacent_links_at_size_0 = False # sometimes interferes with layouts
separate_adjacent_links_at_other_sizes = True
# Fonts: (cjk_fonts is listed first so it can be used in both serif_fonts and sans_serif_fonts)
cjk_fonts = "Lantinghei SC, AppleGothic"
# AppleGothic must be listed or Korean is broken on Mac OS 10.7
# Lantinghei SC was introduced to OS X in 10.9, which is handy because the previously-good STSong font (which was the system default for Simplified Chinese) broke on 10.9: it renders badly with antialiasing turned off at 20px, e.g. missing the horizontal stroke on U+95E8. So we set Lantinghei SC for 10.9 but fall back to STSong on 10.8/10.7/etc (I don't think we need to explicitly say STSong, and there are advantages in not doing so, e.g. the TODO below about :lang(ja) is not relevant for pre-10.9 systems)
# TODO: for :lang(ko) and :lang(ja) we had better put AppleGothic and a Japanese font like YuGothic first (before Lantinghei) - see below re U+8D77, U+95E8 etc. Pity can't read the system preferences for pages that don't set a CJK value of LANG. This :lang exception needs to be done separately for every element that has a font-family, to avoid corrupting headings etc.
# Other Mac CJK fonts to be aware of: MingLiU prefers full 'Traditional' forms of characters where Trad/Simp has same Unicode value (e.g. U+8D77 'qi3' has an extra vertical stroke making the 'ji' component look more like a 'si'); renders OK on Mac OS 10.9 at 20px without antialias, but might not always be present (the ttf is installed to /Library/Fonts/Microsoft by MS Office and is not present on machines without MS Office). Arial Unicode MS (present on both 10.7 and 10.9) has some issues with baselines not lining up e.g. in the word 'zhen1li3' U+771F U+7406; it prefers Simplifed Chinese forms (e.g. U+8D77 uses 'ji', and U+95E8 is the Chinese rather than the Japanese simplification). GB18030 Bitmap (NISC18030) might work at 16px, 32px etc, but scales badly to other sizes. "Hei" has irregular stroke widths in 10.9 20px no-antialias, but otherwise OK
serif_fonts = "Times New Roman, times, utopia, /* charter, */ "+cjk_fonts+", serif" # TNR is listed first for the benefit of broken Xft systems that need the MS fonts to make them look OK. Shouldn't have any effect on other systems.
sans_serif_fonts = "helvetica, arial, verdana, "+cjk_fonts+", sans-serif" # (TODO: do we want different cjk_fonts here?)
pinyin_fonts = "FreeSerif, Lucida Sans Unicode, Times New Roman, DejaVu Sans, serif" # try to get clear tone marks (but DejaVu Sans must be low priority because it results in disappearing text on some buggy Safari versions under Mac OS 10.7)
browser_is_Firefox_73 = False # set this to True ONLY if you are using Firefox 73, to work around bug 1616243. Do NOT set it to True on Firefox 74+ as it will make checkboxes unreadable.
# ---- End of options (but read on for debugging) ----
# DEBUGGING BY BINARY CHOP: If a complex stylesheet exhibits
# a behaviour you weren't expecting (maybe due to a browser
# bug but maybe not) then it might not be obvious how to fix
# it. This program has a debug mode that helps you do it by
# binary chop. Run like this:
# python css-generate.py chop
# which will generate a single debug .css file with half
# the attributes disabled. Try that debug .css; if the
# problem persisted, then do
# python css-generate.py chop 1
# or if the problem did not persist, do
# python css-generate.py chop 0
# and then try again. Then add another 1 or 0 depending on
# whether or not the problem persisted the next time, e.g.
# python css-generate.py chop 01
# Hopefully it will narrow down the problem to one thing.
# You will see which one that is from the debug messages
# it prints on standard output (these will appear instead of
# the HTML index of stylesheets that is usually generated).
chop_extra_verification = True # If True, we'll take an extra step to verify each chop result (by checking we get the opposite in the inverse set) before further subdivisions
# For more desperate debugging cases, you can try:
# python css-generate.py desperate-debug
# which will generate a large number of stylesheets, each
# adding one more rule and not combining. (Note that some
# rules at the beginning and end of the stylesheet will be
# there unconditionally though.)
# TODO consider forcing "display" to its normal value (not "none")
# if some sites are going to use stock scripts that switch them on and
# off every few seconds inadvertently making the rest of the page dance around
# ---- End of debugging info, code follows ----
try: from css_generate_config import *
except ImportError: pass
try: any
except: # backward compatibility with Python 2.1:
def any(l):
for i in l:
if i: return True
try: dict
except:
def dict(l):
r = {}
for k,v in l:
r[k] = v
return r
try: set
except:
def set(l): return l
try: sorted
except:
def sorted(l,cmpFunc=None):
r = l[:]
r.sort(cmpFunc)
return r
try: reduce # Python 2
except: # Python 3
from functools import reduce, cmp_to_key
_builtin_sorted = sorted
def sorted(l,theCmp=None):
if theCmp: return _builtin_sorted(l,key=cmp_to_key(theCmp))
else: return _builtin_sorted(l)
def cmp(a,b): return (a>b)-(a<b)
try: bytes # Python 3 and newer Python 2
except: bytes = str # older Python 2
try: unicode # Python 2
except: unicode = str # Python 3
if type(colour_schemes_to_generate) in (str,unicode):
import glob
colour_schemes_to_generate = reduce(lambda x,y:x+eval(open(y).read()), glob.glob(colour_schemes_to_generate), [])
def do_one_stylesheet(pixelSize,colour,filename,debugStopAfter=0):
outfile = open(outputDir+os.sep+filename,"w")
smallestHeadingSize = pixelSize*5.0/6.0
largestHeadingSize = pixelSize*10.0/6.0
# In the settings below, beginning with * means it will
# be omitted from the "pixelSize 0" option (i.e. leave
# site's size/layout alone and just changing colours),
# and anything beginning with ** means it will be
# included ONLY in the "pixelSize 0" option.
defaultStyle={
"*font-family":serif_fonts,
"*font-size":"%.1fpx" % pixelSize,
"color":colour["text"],
"background":colour["background"], # background-color is handled by aliases
# We have to specify more or less everything even if
# it's OK by default, otherwise a web author's
# stylesheet may override the default and cause a
# not-so-good author/user stylesheet combination.
"*font-style":"normal",
"*font-weight":"normal",
"*font-variant":"normal",
"*font-size-adjust":"none",
"*zoom":"normal", # MSIE thing; some other browsers understand it too; can give websites another chance to shrink the text if we don't set it here
"background-image":"none",
"*letter-spacing":"normal",
"*line-height":"normal",
"*width":"auto",
"*height":"auto",
"*border-width":"0.05em",
"*border-radius":"0.05em",
"*-moz-border-radius":"0.05em",
"*-webkit-border-radius":"0.05em",
"*-webkit-font-smoothing":"none", # font smoothing doesn't work so well on large-print low-resolution dark-background displays...
"*-moz-osx-font-smoothing":"auto","*font-smooth":"never", # -moz-osx-font-smoothing overrides font-smooth on Firefox 25+; "never" would be better, but at least Ffx 29 and 45.9-ESR doesn't support it and falls back to the SITE's spec :-( (greyscale is worse than auto in large print low resolution) (only auto and greyscale are supported in firefox-45.0esr/layout/style/nsCSSProps.cpp, see gfx/thebes/gfxMacFont.cpp the only way to turn it off is if it's 12pt or smaller and you've said so in Apple Preferences and enabled about:config's gfx.use_text_smoothing_setting; still no good for over 12pt)
"*-webkit-text-stroke":"0",
"*-webkit-animation":"none","*-o-animation":"none","*-moz-animation":"none","*animation":"none",
"*-webkit-animation-name":"none","*-o-animation-name":"none","*-moz-animation-name":"none","*animation-name":"none",
"*position":"static",
"*visibility":"visible", # because we're forcing position to static, we must also force visibility to visible otherwise will get large gaps. Unfortunately some authors use visibility:hidden when they should be using display:none, and CSS does not provide a way of saying '[visibility=hidden] {display:none}'
"*float":"none","*clear":"none",
"*min-height":"0px",
"*max-height":"none",
"*max-width":"none", # see comments below on "max-width"
"*min-width":"0px",
"*text-decoration":"none",
"text-shadow":"none",
"mix-blend-mode":"normal",
"*text-align":"left", # not full justification
"*margin":"0px",
"*padding":"0px",
"*text-indent":"0px",
"*white-space":"normal", # not "nowrap"
"*cursor":"auto",
"*overflow":"visible", # the default. NOT "auto" - it may put the scroll bar of a table off-screen at the bottom. If (e.g.) "pre" overflows, we want the whole window to be scrollable to see it.
"filter":"none","-webkit-filter":"none","-moz-filter":"none","-o-filter":"none","-ms-filter":"none", # don't allow 'invert' etc unless we say
"*opacity":"1",
"*mask-image":"none","*-webkit-mask-image":"none",
"-moz-appearance":"none", # DON'T * this, it can lead to white-on-white situations so we need it for colour changes not just size changes
"*-webkit-hyphens":"manual", # auto hyphenation doesn't always work very well with our fonts (TODO: manual or none? manual might be needed if devs put breakpoints into very long words)
"*-moz-hyphens":"manual",
"*-ms-hyphens":"manual",
"*hyphens":"manual",
"*table-layout":"auto",
"user-select":"text","-moz-user-select":"text","-webkit-user-select":"text", # don't allow making things non-selectable, as selection might help keep track of things (TODO: still have user-select:none for buttons etc?)
"*flex-wrap":"wrap", # needed for giant print or small windows
"*grid-column-start":"auto","*grid-column-end":"auto","*grid-row-start":"auto","*grid-row-end":"auto",
"*grid-auto-columns":"auto","*grid-auto-rows":"auto","*grid-auto-flow":"row",
"*grid-template-columns":"none","*grid-template-rows":"none",
"*justify-content":"flex-start","*align-items":"flex-start","*align-content":"flex-start","*align-self":"flex-start", # hopefully makes things more findable
"*flex-basis":"auto", # giant print or small windows can cause long words to overflow 'flex' layouts that specify small pixel widths
"*flex":"0 1 auto", # may prevent margin overflow
"*-moz-column-count":"1", # see below for column-count (NOT webkit, Chrome/57 bug)
}
for css3Thing,value in [
# Get rid of "flip boxes"...
("transform","none"),
("transform-style","flat"),
("backface-visibility","visible"),
("transition-property","none")]:
for browser in ["",'-o-','-ms-','-moz-','-webkit-']:
defaultStyle['*'+browser+css3Thing] = value
# have to explicitly set for every type of element,
# because wildcards don't always work and inheritance can
# be overridden by author stylesheets resulting in poor
# combinations. NB however we don't list ALL elements in
# mostElements (see code later).
mostElements="a,blockquote,caption,center,cite,code,col,colgroup,html,iframe,pre,body,div,p,input,select,option,textarea,table,tr,td,th,h1,h2,h3,h4,h5,h6,font,basefont,small,big,span,ul,ol,li,i,em,s,strike,nobr,tt,samp,kbd,b,strong,dl,dt,dd,blink,button,address,dfn,form,marquee,fieldset,legend,listing,abbr,q,menu,dir,multicol,img,plaintext,xmp,label,sup,sub,u,var,acronym,object,embed,canvas,video".split(",")
html5Elements = "article,aside,bdi,command,details,summary,figure,figcaption,footer,header,hgroup,main,mark,meter,nav,progress,section,date,time,del,ins,svg,output,thead,tbody".split(",") # (not sure about 'date' but it's used by some sites)
rubyElements = "ruby,rt,rp,rb".split(",") # NOT counted in mostElements
html5Elements += ['text','text > tspan'] # used within svg, sometimes for nothing more than effect (unfortunately there doesn't seem to be a way of ensuring the containing svg is displayed large enough, but truncation is better than having the text go underneath other elements)
mostElements += html5Elements
mostElements += ['location'] # site-specific hack for lib.cam.ac.uk
css={} ; printOverride = {}
webkitScreenOverride = {} ; geckoScreenOverride = {} ; msieScreenOverride = {}
webkitGeckoScreenOverride = {} ; webkitMsieScreenOverride = {} ; geckoMsieScreenOverride = {}
for e in mostElements+rubyElements:
css[e]=defaultStyle.copy()
printOverride[e] = {"color":"black","background":"white"}.copy()
printOverride[e]["*font-size"] = "12pt" # TODO: option?
geckoMsieScreenOverride[e] = {"*column-count":"1"} # not Webkit (PageUp/PageDown bug in Chrome57 etc)
# but there are some exceptions:
for e in rubyElements: del css[e]["*text-align"]
if not pixelSize:
# Size = unchanged, much-reduced layout changes. But a LOT of "modern" sites assume they can stack elements (DIV, A, etc) with some transparency. This can result in obscuring text if we have solid colour backgrounds. But we don't want it completely transparent (unless we can confirm that's correct for a site-specific hack) as we probably won't be able to catch all UI elements as exceptions.
for e in css.keys():
if 'background' in css[e] and not e in ['html','body']: css[e]['background'] = colour["translucent_background_compromise"]
# Also do this for Firefox's PDF viewer:
css["div.pdfViewer div.page > div.canvasWrapper + div.textLayer"]={"opacity":"1"}
del css['svg']['*font-size'] # doesn't make sense to override, as it's subject to the resize of the whole SVG (usually an enlargement)
del printOverride['svg']['*font-size']
for e in ['text','text > tspan']: # may help with SVG Lilypond output
del css[e]["*font-size"],css[e]["*font-family"],css[e]["*font-weight"],css[e]["*font-variant"]
del printOverride[e]["*font-size"]
for k in list(css["img"].keys()):
if k.startswith("background"): del css["img"][k] # e.g. WhatsApp emoji uses a single image with positioning (and we want this to work if size=unchanged)
for k in list(printOverride["img"].keys()):
if k.startswith("background"): del printOverride["img"][k]
css["rt:lang(cmn-hans),rt:lang(zh)"]={"*font-family":pinyin_fonts}
del css["rt"]["*padding"] # some sites omit space between ruby elements and make up for it by setting padding on the rt elements: let that through
del css["ruby"]["*padding"];del css["rb"]["*padding"] # might as well do this too
for w in ["*width","*height"]: del css["svg"][w] # 'auto' is very often wrong for svg, and some browsers' understanding of specificity results in our viewBox overrides not working if auto is set here
for t in ["textarea","html","body","input"]:
css[t]["*overflow"] = "auto"
# 'html' is there for IE7. But Firefox needs it to be 'visible'. See hack at end.
# TODO previously excluded 'body' (and deleted 'body' overflow 'visible')
# as it somehow disables keyboard scrolling in IE7, however do need to
# set 'body' to "auto' to work around CouchDB's "overflow:none" in both
# 'html' and 'body' (at least in Firefox); need to find an IE7 to re-test
# (if broken, consider re-instating the delete and move body:auto to the
# non-IE7 override hack at end)
# del css["body"]["*overflow"] # Do not set both "body" and "html" in IE7 - it disables keyboard-only scrolling!
del css["input"]["*overflow"] # for IE 6 and possibly 8 overprinting too-long input type="text" with a horizontal scrollbar
for e in [ # remove height:auto and width:auto from:
"object","embed", # should not be forced to 'auto' as that can sometimes break Flash applications (when the Flash application is actually useful)
"img", # can break on some versions of IE (is added back in for other browsers below)
"input", # can result in 0 on some versions of Gecko (Firefox 60-62 are affected, at least with checkboxes and radio buttons)
]:
del css[e]["*width"], css[e]["*height"]
css[exclude_ie_below_9+"img"]={"*width":"auto","*height":"auto"} # but we can at least add img back on non-IE browsers (TODO: which versions of IE were affected?) - we DO need to specify this, to cope with sites that do silly things like set image height to something e+7 pixels and expect layering to compensate
css["textarea"]["*width"]="100%" # not "auto", as that can cause Firefox to sometimes indent the textarea's contents off-screen
css["frame"]={}
for e in ["frame","iframe"]: css[e]["*overflow"]="auto" # to override 'scrolling=no' which can go wrong in large print (but this override doesn't always work)
css["sup"]["*vertical-align"] = "super" # in case authors try to do it with font size instead
css["sub"]["*vertical-align"] = "sub"
css["marquee"]["*-moz-binding"]="none" # don't scroll marquee elements
css["marquee"]["*display"]="block"
css["center"]["*text-align"] = "center"
for s in ['s','strike']: css[s]["*text-decoration"]="line-through"
# TODO: not sure if really want this for the 's' alias of 'strike', since some sites e.g. http://www.elgin.free-online.co.uk/qp_intro.htm (2007-10) use CSS to override its presentation into something other than strikeout
css['span[style="text-decoration:line-through"],span[style="text-decoration:line-through;"]']={"*text-decoration":"line-through"} # used on some sites
# Margin exceptions:
css["body"]["*margin"]="1ex %.1fpx 1ex %.1fpx" % (pixelSize*5/18.0,pixelSize*5/18.0) # keep away from window borders
for i in "p,multicol,listing,plaintext,xmp,pre".split(","): css[i]["*margin"]="1em 0"
css["li > p:first-child"]={"*margin-top":"0"} # I'm not sure P is intended to go inside LI like this (especially when there's only one paragraph), but GitHub does it as of 2017 so I suppose we should try to cope (positioning of the circle is still a bit sub-optimal though)
listStuff="ul,dir,menu,dl,li".split(",") # not ol, leave that as margin 0px - see ol > li below
for l in listStuff:
css[l]["*margin"]="0 1em 0 %.1fpx" % (pixelSize*10/18.0)
listStuff.remove("li")
for l in listStuff:
for l2 in listStuff:
css[l+" "+l2]={"*margin":"0px"}
css["ol > li"] = {"*list-style-position":"inside","*margin":"0px","*padding-left":"1em","*text-indent":"-1em"} # helps when numbers get very large
css["blockquote"]["*margin"]="1em 4em"
css["blockquote[type=cite]"]={"*margin":"1em 0px","*padding":"1em 0px 0px 0px"}
css["dd"]["*margin"]="0em 2em"
for t in ["th","td"]:
css[t]["*padding"]="%.1fpx" % (pixelSize/18.0,)
css[t+'[align^="right"]'] = {"*text-align":"right"}
# Don't say white-space normal on user input elements or pre
# Galeon 1.25: we also have to exclude "body" and "div" for some reason
# TODO: is it REALLY a good idea to leave 'div' on this list?
for e in "pre,input,textarea,body,div".split(","): del css[e]["*white-space"]
for e in "font,code,tt,span,b".split(","): css["pre "+e]={"*white-space":"inherit"} # some mailing lists etc have "font" within "pre", and some sites have "code" within "pre"
# Monospaced elements
for t in "pre,code,tt,kbd,var".split(","): css[t]["*font-family"]="monospace"
# and 'samp' let's have sans-serif
for t in "samp".split(","): css[t]["*font-family"]=sans_serif_fonts
css["spacer"]={"*display":"none"} # no point in keeping the spacers now we've changed the layout so much
css["a"]["*display"] = "inline" # some sites override it to block, which might have worked OK in their CSS's context but it's not so good in ours
# max-width (if supported, i.e. not IE6) can reduce left/right scrolling -
# if one line's linebox needs to expand (e.g. due to a long word), then we
# don't want to expand the whole block (e.g. the table cell) and all its
# other line boxes. BUT: if a max-width'd TD (or even a DIV etc) does
# have to overflow in a big way (e.g. due to map images), and there is
# something else to the right (e.g. nested tables with rowspans),
# overprinting can result, unless also using 'overflow:auto' which can
# create too many nested or offscreen scrollbars. No way to say "use this
# width for internal formatting, then expand to overflows for external
# bounds" or "use this width if you are a leaf node with mostly text" or
# whatever. Only alternative for now is to say "none" and have to
# horizontally scroll. (Even "p" is not safe to set - consider a
# table containing a p containing another table that overflows.)
# (If set td max-width to "-moz-available" later in the stylesheet, will
# cause Firefox 3 to do less overprinting than it would have done while
# Firefox 2 takes the previous one and does a better job incorrectly, but
# then there are still some instances of overprinting regardless of
# version etc., especially on mapping sites)
# (Note: On Firefox 2 (not 3), max-width works in a nice way, see Bugzilla
# bug report at https://bugzilla.mozilla.org/show_bug.cgi?id=452840 - so
# you may want to uncomment the following if you are particularly on Firefox 2.)
# for e in mostElements: css[e]["*max-width"]=("%.1fpx" % min(1200,pixelSize*470/18))
# Links stuff - must be before bold/italic colour overrides:
for linkInside in ",font,big,small,basefont,br,b,i,u,em,cite,strong,abbr,span,div,code,tt,samp,kbd,var,acronym,h1,h2,h3,h4,h5,h6".split(",")+rubyElements:
for lType in [":link",":visited","[onclick]",
".button", # used by some JS applications
]:
css["a"+lType+" "+linkInside]={"color":colour["link"],"text-decoration":"underline","cursor":"pointer"}
printOverride["a"+lType+" "+linkInside]={"color":"#000080"} # printing: links in blue might be useful for sending PDFs to others, but make it a dark blue so still readable if printed in black and white (don't try to ensure page is ALWAYS black and white: that can't be done w/out suppressing images. User needs to suppress colour at print time. But ensure legible choice of shading when that happens.)
css["a"+lType+":hover "+linkInside]={"background":colour["hover-bkg"]}
css["a"+lType+":active "+linkInside]={"color":"red","text-decoration":"underline","cursor":"pointer"}
if linkInside in ["b","i","em","u","strong"] and not css[linkInside]["color"]==colour["text"]: css["a"+lType+" "+linkInside]["color"]=css[linkInside]["color"]
css["a:visited "+linkInside]["color"]=colour["visited"]
# set cursor:pointer for links and ANYTHING inside them (including images etc). The above cursor:auto should theoretically do the right thing anyway, but it seems that some versions of Firefox need help.
for linkInside in mostElements+rubyElements:
for lType in [":link",":visited","[onclick]"]:
key="a"+lType+" "+linkInside
css.setdefault(key,{})["cursor"]="pointer"
if not linkInside in rubyElements: css[key]["*display"]="inline" # some sites have 'div' or do JS things with 'span'...
# Italic and bold:
for i in "i,em,cite,address,dfn,u".split(","):
css[i+" span"]={
"*font-family":sans_serif_fonts,
"color":colour["italic"]}
printOverride[i+" span"]={"color":"black"}
css[i].update(css[i+" span"])
for i in "b,strong".split(","):
css[i+" span,"+i+" kbd"]={
"*font-weight":"bold",
"color":colour["bold"]}
printOverride[i+" span,"+i+" kbd"]={"color":"black"}
css[i].update(css[i+" span,"+i+" kbd"])
css["acronym"]["color"]=colour["bold"]
css["abbr"]["color"]=colour["bold"]
css["ins"]["color"]=colour["italic"]
css["del"]["color"]=colour["italic"]
css["del"]["text-decoration"]="line-through"
# Some browsers might start styling abbr by default but not acronym. Some older browsers might understand acronym title= but not abbr title=, so some sites might try to use acronym= for backward compatibility, but given that this must be for nonessential information anyway (as many simpler browsers don't support either) it probably makes sense to prefer abbr now (if it might be displayed by default on a greater number of modern browsers) unless the webmaster wants to emulate the browser in CSS.
css["acronym"]["border-bottom"]="1px dotted"
css["abbr"]["border-bottom"]="1px dotted"
# Headings stuff (must be after italic/bold):
indent = 0
for h in range(6):
el="h%d" % (h+1)
css[el]["*font-family"]=sans_serif_fonts
size = (largestHeadingSize-h*(largestHeadingSize-smallestHeadingSize)/(6-1.0))
css[el]["*font-size"]="%.1fpx" % size
css[el]["*font-weight"]="bold"
# ensure 'h1 strong' etc inherits family (but not necessarily colour):
for i in ['strong','em','i','b']:
css[el+" "+i]=css[el].copy()
css[el+" "+i]["color"]=css[i]["color"]
printOverride[el+" "+i]={"color":"black"}
# now for heading colour:
css[el]["color"]=colour["headings"]
printOverride[el]={"color":"black"}
# ensure links in headings inherit size and family:
css[el+" center"]=css[el].copy() # rather than the default for 'center'
css[el+" a"]=css[el].copy() # because it's usually A NAME (in the case of HREF, the specificity of a:link should be greater)
css[el+" abbr"]=css[el].copy() ; del css[el+" abbr"]["*text-decoration"]
css[el+" span"]=css[el].copy()
css[el+" a b"]=css[el].copy()
printOverride[el+" center"]=printOverride[el].copy()
printOverride[el+" a"]=printOverride[el].copy()
printOverride[el+" abbr"]=printOverride[el].copy()
printOverride[el+" span"]=printOverride[el].copy()
printOverride[el+" a b"]=printOverride[el].copy()
# and now (AFTER the above) set margins on headings
if h: indent += size
css[el]["*margin"]="0px 0px 0px %.1fpx" % indent
# Images and buttons:
css["img:not(.emoji)"]={"background":colour["image_transparency_compromise"]} # see WhatsApp exception above
# Exception needed for MediaWiki TeX images
# (they tend to be transparent but with antialiasing that
# assumes the background will be white)
css["body.mediawiki img.tex"]={"background":"white"}
# (note however it might be possible to set the wiki to
# display maths as real TeX or something instead)
if not colour["background"]=="white": css["body.mediawiki img.tex"]["border"]="white solid 3px" # to make sure letters near the edge are readable if the rest of the page has a dark background
if "image_opacity" in colour.keys():
del css["img"]["*opacity"],css["img"]["filter"]
css["img"]["opacity"]="%g" % colour["image_opacity"]
css["img"]["filter"]="alpha(opacity=%d)" % int(colour["image_opacity"]*100) # for IE8 and below
if colour["image_opacity"]<0.9: css["img:hover"] = css["a:hover img"]={"opacity":"0.9","filter":"alpha(opacity=90)"}
css["button"]["background"]=colour["button-bkg"]
printButtonBackground = "#e0e0e0" # light grey, TODO: option
printOverride["button"]["background"]=printButtonBackground
css['div[role="button"]']={"background":colour["button-bkg"]} # for Gmail 2012-07 on "standard" view (rather than "basic HTML" view). "Standard" view might work for people who want the "unchanged" size.
printOverride['div[role="button"]']={"background":printButtonBackground}
if "alt-backgrounds" in colour.keys():
# override specificity of alt-backgrounds div:nth-child
css['html body div[role="button"]'] = css['div[role="button"]']
printOverride['html body div[role="button"]'] = {"background":printButtonBackground}
css["input[type=submit]"]={"background":colour["button-bkg"]}
css["input[type=button]"]={"background":colour["button-bkg"]}
css["input[type=reset]"]={"background":colour["reset-bkg"]}
printOverride["input[type=submit]"]={"background":printButtonBackground}
printOverride["input[type=button]"]={"background":printButtonBackground}
printOverride["input[type=reset]"]={"background":printButtonBackground}
for f in ["select","input","textarea","button"]:
k = "html "+f+'[disabled]' # must include 'html' so more specific than above (TODO: or :not(:empty) if got enough CSS?)
if f=='input': k += ", html "+f+'[readonly]'
css[k]={"background":colour["form_disabled"]}
printOverride[k]={"background":printButtonBackground} # TODO: or something else?
# Separate adjacent links (CSS2+)
if (pixelSize and separate_adjacent_links_at_other_sizes) or (not pixelSize and separate_adjacent_links_at_size_0):
for l in [":link",":visited","[onclick]"]:
css[exclude_ie_below_9+"a"+l+":before"]={"content":'"["',"color":colour["text"],"text-decoration":"none","white-space":"nowrap"}
css[exclude_ie_below_9+"a"+l+":after"]={"content":'"]"',"color":colour["text"],"text-decoration":"none","white-space":"nowrap"}
# make sure the hover colour includes :before and :after - this is needed if the :before/:after text is changed by site-specific hacks etc (to cope with empty links)
css[exclude_ie_below_9+"a"+l+":hover:before"]={"background":colour["hover-bkg"]}
css[exclude_ie_below_9+"a"+l+":hover:after"]={"background":colour["hover-bkg"]}
printOverride[exclude_ie_below_9+"a"+l+":before"]={"color":"black"} # TODO: option to delete the "[" "]" content also?
printOverride[exclude_ie_below_9+"a"+l+":after"]={"color":"black"}
# Avoid style overrides from :first-letter, :first-line,
# :before and :after in author's CSS. However be careful
# which elements you do this because of browser bugs.
firstLetterBugs_multiple=[
"input","select","option","textarea","table","colgroup","col","img", # probably best to avoid these
"div", # Gecko messes up textarea when enter multiple paragraphs; Safari has text selection visibility problem see below
"svg","text","text > tspan","object", # doesn't make sense, and can cause confusion
]
firstLetterBugs_geckoOnly=[
# none here for now
]
firstLetterBugs_webkitOnly=[
# The following cause text selection visibility problems in Webkit / Safari 5/6 (cannot be worked around with :first-letter::selection)
# (added 'caption' 2022: causing selection issues in Safari 15)
# (+ Chrome 12 bug - OL/LI:first-letter ends up being default size rather than css size; harmless if have default size set similarly anyway)
"main", # Safari 17.3
"label","address","p","ol","ul","li","pre","code","body","html","h1","h2","h3","h4","h5","h6","form","th","tr","td","dl","dt","dd","b","blockquote","section","header","footer","center","article","span","aside","figure","caption","figcaption","time","em"
]
firstLetterBugs_msie=["a"]
assert not(any([(x in firstLetterBugs_geckoOnly or x in firstLetterBugs_webkitOnly or x in firstLetterBugs_msie) for x in firstLetterBugs_multiple]) or any([(x in firstLetterBugs_webkitOnly or x in firstLetterBugs_msie) for x in firstLetterBugs_geckoOnly]) or any([(x in firstLetterBugs_msie) for x in firstLetterBugs_webkitOnly])), "Error: firstLetterBugs item in more than one category"
firstLineBugs=[
"div", # on firefox 2 causes some google iframes to occlude page content
"svg","text","text > tspan","object", # doesn't make sense, and can cause confusion
"input","select","option","textarea","table","colgroup","col","img",
"td","th", # causes problems on Firefox 2 if there's a form inside
"a", # causes problems in IE
"span", # sometimes causes crashes in Opera 12
"li", # sometimes causes crashes in Opera 12 (note this might be sacrificing some control, if someone does try a li:first-line override)
# To be safe, could add other inline-etc tags mentioned in mostElements:
"label","nobr","tr","ol","ul","abbr","acronym","dfn","em","strong","code","samp","kbd","var","b","i","u","small","s","big","strike","tt","font","cite","q","sub","sup","blink","button","command","dir","embed","object","fieldset","iframe","marquee","basefont","bdi","canvas","time",
# TODO: other :first-line, :first-letter and :hover overrides can still crash Opera 12 on some sites. Have suggested in index.html that the user replaces :first with :girst and :hover with :gover when installing one of these CSS files to Opera.
]
inheritDic={"color":"inherit","background":"inherit","*letter-spacing":"inherit","*font-size":"inherit","*font-family":"inherit"}
# (NB must say inherit, because consider things like p:first-line / A HREF... - the first-line may have higher specificity.
# If IE7 seems to be getting first lines and first letters wrong, check that Ignore Document Colours is NOT set - "document" can include parts of the CSS. and try toggling high-contrast mode twice.)
for e in mostElements:
if not e in firstLetterBugs_multiple:
if e in firstLetterBugs_geckoOnly: dictToAddTo = webkitScreenOverride # or webkitMsieScreenOverride, but would have to test MSIE versions
elif e in firstLetterBugs_webkitOnly: dictToAddTo = geckoScreenOverride # or geckoMsieScreenOverride, but would have to test MSIE versions
elif e in firstLetterBugs_msie: dictToAddTo = webkitGeckoScreenOverride
else: dictToAddTo = css
dictToAddTo[e+":first-letter"]=inheritDic.copy()
if not e in firstLineBugs: css[e+":first-line"]=inheritDic.copy()
for i in map(lambda x,e=e:exclude_ie_below_9+e+x,[":before",":after"]):
css[i]=defaultStyle.copy()
if e=="img": del css[i]["background"]
else: css[i]["background"]="transparent" # essential for 0.css where the pseudo-element might be repositioned with different z-index; not supported by IE below 9 but neither are pseudo-elements in general (we're on exclude_ie_below_9 anyway)
for mp in ["*margin","*padding"]:
if not css.get(e,{}).get(mp,"")==css[i][mp]:
del css[i][mp] # as not sure how browsers would treat a different margin/padding in :before/:after. But DO keep these settings for the 0px elements, because we DON'T want sites overriding this and causing overprinting.
# and also do this for no specific element:
for i in map(lambda x:exclude_ie_below_9+x,[":before",":after"]):
css[i]=defaultStyle.copy() # (especially margin and padding)
css[i]["background"]="transparent" # see above
# inheritDic may also be useful for common child elements of links, highlights etc:
for e in rubyElements:
css[e].update(inheritDic)
del printOverride[e]
# CSS 2+ markup for viewing XML+CSS pages that don't use HTML. Not perfect but should be better than nothing.
xmlKey=":root:not(HTML):not(page):not(svg), :root:not(HTML):not(page):not(svg) :not(:empty)"
# Careful not to use the universal selector, because it can mess up Mozilla's UI.
# :not(page) is an important addition for recent versions of Firefox whose Preference pages start with 'page' (can be rendered invisible if apply whole of defaultStyle to it).
css["page:root *"]={"background-color":colour["background"]} # to normalise recent-Firefox preferences pages (without this, some parts do and some don't; result can look too stark). Tested in Firefox 45.4.
css[xmlKey]=defaultStyle.copy()
del css[xmlKey]["*text-decoration"] # because this CSS won't be able to put it back in for links (since it doesn't know which elements ARE links in arbitrary XML)
# Exception to above for Mozilla scrollbars:
css[":root:not(HTML):not(page):not(svg) slider:not(:empty)"]={"background":"#301090"}
# and Firefox icons: (TODO: this may need re-testing on affected versions of Firefox; needed to add :not(svg) or get link-coloured parts in object-embedded SVG files in Safari)
css[":root:not(HTML):not(svg) *"]={"-moz-context-properties":"fill,fill-opacity","fill":colour["link"],"fill-opacity":"1"}
checkbox_scale = int(pixelSize/16)
for iType in ["checkbox","radio"]:
iKey = "input[type="+iType+"]"
if checkbox_scale > 1: css[iKey]={"transform":"scale(%d,%d)" % (checkbox_scale,checkbox_scale),"margin":"%dpx"%(checkbox_scale*6)} # margin not padding (browser problems)
else: css[iKey]={}
css[iKey]['-webkit-appearance']=iType
if browser_is_Firefox_73: css[iKey]['-webkit-appearance'] += ' !important; -moz-appearance: none'
if pixelSize:
# In many versions of firefox, a <P ALIGN=center> with an <IFRAME> inside it will result in the iframe being positioned over the top of the main text if the P's text-align is overridden to "left". But missing out text-align could allow websites to do full justification. However it seems OK if we override iframe's display to "block" (this may make some layouts slightly less brief, but iframes usually need a line of their own anyway)
css["iframe"]["*display"]="block"
# and if we're doing that, we might as well use the full width:
css["iframe"]["*width"]="100%"
# The following may help a little as well: make iframes 50% transparent so at least we can see what's under them if they do overprint (depends on the browser and the site; apparently the IFRAME's height can be treated as close to 0 when it's not) (fixed? keeping this anyway just in case)
css["iframe"].update({"*filter":"alpha(opacity=50)","*opacity":"0.5"})
# float exceptions for img align=left and align=right (might as well)
css["img[align=left]"]={"*float":"left"}
css["img[align=right]"]={"*float":"right"}
# Selection (CSS3)
if "selection-bkg" in colour.keys():
css["::selection"] = {"background":colour["selection-bkg"]}
css["::-moz-selection"] = {"background":colour["selection-bkg"]}
css['input[type=search]'] = {
"-webkit-appearance":"textfield", # searchbox forces background:white which may conflict with our foreground
}
css['input[type=search]']['-webkit-appearance'] += ' !important; -moz-appearance: none' # needed on both Firefox 73 and 86. In Firefox 86 with -moz-appearance and -webkit-appearance textfield, specifying border-width 1px somehow makes all type=search fields white. Not specifying border-width (but setting a border-radius) will still leave some type=search turning white, even when it's unclear the site itself is setting borders, so more must be at play here. Setting appearance to none makes none of them white, even when border-width is set, and even when border-radius is not set. -moz-appearance overrides -webkit-appearance only if specified after it.
css['select']['-webkit-appearance']='listbox' # workaround for Midori Ubuntu bug 1024783
css['select']['-webkit-appearance'] += ' !important; -moz-appearance: none' # even if not browser_is_Firefox_73
css['select']['background']=colour["select-bkg"]
printOverride['select']['background']=printButtonBackground # TODO: or something else?
if "alt-backgrounds" in colour.keys():
css['td:nth-child(odd),div:nth-child(odd)'] = {"background":colour["alt-backgrounds"][0]}
printOverride['td:nth-child(odd),div:nth-child(odd)'] = {"background":"white"} # TODO: or a very light grey?
if len(colour["alt-backgrounds"])>1:
css['td:nth-child(even),div:nth-child(even)'] = {"background":colour["alt-backgrounds"][1]}
printOverride['td:nth-child(even),div:nth-child(even)'] = {"background":"white"} # TODO: or another very light grey?
for k in list(css.keys()):
if css[k].get("background","")==colour["background"] and not k in ["html","body"]: css[k]["background"]="inherit"
# Make definition lists a bit more legible, including when there is more than one definition for one term
css['dd+dd']={'*padding-top':'0.5ex','*margin-top':'1ex','*border-top':'thin dotted grey'}
css['dt'].update({'*padding':'0.5ex 0px 0px 0px','*margin':'1ex 0px 0px 0px','border-top':'thin grey solid'})
css['hr']={"color":"grey","border-style":"inset"} # prevent pages from changing the colour of horizontal rules, especially to black if we have a black background (sometimes used within tables to mimic fraction lines in formulae)
for aside in ['aside','figure']: css[aside]['border']="thin "+colour["italic"]+" solid" # might help sometimes
css['body > pre:only-child']={'*white-space':'pre-line','*font-family':serif_fonts} # this might make Gopher pages easier to read in Firefox's "OverbiteFF" (unless ASCII art is in use); NB on some firefox versions it slows down the loading of text/plain URLs and chrome://browser/skin/browser.css etc
css['body > form[action="man:"] + pre']={'*white-space':'pre-line','*font-family':serif_fonts} # ditto for Bwana manpage-viewing plugin for Mac OS X browsers
for pt in '::-webkit-input-placeholder,:-moz-placeholder,::-moz-placeholder,:ms-input-placeholder,::placeholder,:placeholder-shown'.split(","): css[pt] = {"color":colour["form_disabled"]}
# Don't blur GIFs and PNGs if showing images in high DPI (taken from https://developer.mozilla.org/en-US/docs/Web/CSS/image-rendering)
css['img[src$=".gif"], img[src$=".png"]'] = { 'image-rendering': '-moz-crisp-edges', 'image-rendering':'-o-crisp-edges','image-rendering':'-webkit-optimize-contrast','image-rendering':'crisp-edges','-ms-interpolation-mode':'nearest-neighbor' }
css['main']['*max-width']='100%' # work around too wide on some sites
css['input']['*max-width']='100%'
css['select']['*max-width']='100%'
# Begin site-specific hacks
if not pixelSize: css['div.icon:not(:empty)']={
# This might contain text for speech that's hidden from graphical browsers, but we want to show it anyway because the graphical icon probably won't show
"height":"auto","width":"auto","text-indent":"0px"} # not -1000px or whatever they did to put the text off-screen
def emptyLink(lType,content,css,printOverride,colour,isRealLink=True,omitEmpty=False,isInsideRealLink=False,undo=False,unchangedSizeOnly=False):
assert not ',' in lType
assert not (undo and content)
if isInsideRealLink: isRealLink = False # overrides
if omitEmpty: eList = [""]
else: eList = [":empty",":blank",":-moz-only-whitespace"]
if unchangedSizeOnly: prefix="**"
else: prefix=""
for empty in eList:
# Fill in the text of an empty link according to
# context (making up for the fact that we're not
# displaying whatever CSS-oriented graphical thing
# the site is showing). lType is the link in context
# and 'content' is our guess of what it should say.
if isRealLink: key = lType+":link"+empty
else: key = lType+empty
if undo: css[key+":before"],css[key+":after"]={},{}
else: css[key+":after"]={prefix+"color":colour["link"]} # (better make sure the colour is right, as it might be in the middle of a load of other stuff)
if content:
if isInsideRealLink: css[key+":after"][prefix+"content"]='"'+content+'"'
else: css[key+":after"][prefix+"content"]='"'+content+']"' # overriding "]"
elif not isInsideRealLink:
if undo: css[key+":after"][prefix+"content"]='""'
else: css[key+":after"][prefix+"content"]='"]"'
if not undo:
printOverride[key+":after"]={prefix+"color":"#000080"}
css[key+":before"]={prefix+"color":colour["link"]}
printOverride[key+":before"]={prefix+"color":"#000080"}
if isRealLink:
key = key.replace(":link",":visited")
if not undo:
css[key+":after"]={prefix+"color":colour["visited"]}
printOverride[key+":after"]={prefix+"color":"#000080"}
css[key+":before"]={prefix+"color":colour["visited"]}
printOverride[key+":before"]={prefix+"color":"#000080"}
else: # not isRealLink
if not isInsideRealLink:
if undo: css[key+":before"][prefix+"content"] = '""'
else: css[key+":before"][prefix+"content"] = '"["'
if undo: css[key]={prefix+"text-decoration":"none",prefix+"cursor":"auto",prefix+"color":colour["text"]}
else:
css[key]={prefix+"text-decoration":"underline",prefix+"cursor":"pointer",prefix+"display":"inline",prefix+"margin":"0px 1ex 0px 1ex",prefix+"color":colour["link"]}
css[key+":before"][prefix+"cursor"] = css[key+":after"][prefix+"cursor"] = "pointer"
for ll in ["",":before",":after"]:
if undo: css[exclude_ie_below_9+key+":hover"+ll]={prefix+"background":"transparent"}
else: css[exclude_ie_below_9+key+":hover"+ll]={prefix+"background":colour["hover-bkg"]}
printOverride[key] = {prefix+"color":"#000080"}
css["div.standardModal-content > div.itemImage:first-child > img"]={"*display":"none"} # 'logo bigger than browser' syndrome
# Hack for Google search results:
css["g-img"]={"*display":"inline","*position":"static"}
css["g-inner-card"]={"background":colour["background"]}
css["span.vshid"]={"*display":"inline"} # TODO: rm * ?
css['img[src^="/images/nav_logo"][alt="Google"]']={"*display":"none"}
css['div.gb_tc.gb_uc.gb_Vb:empty,div.gb_uc.gb_vc.gb_Wb:empty,span.gb_0a:empty']={"*display":"none"} # TODO: if gb = Great Britain then we might need to rewrite this to cover other countries. The div has a :before rule with image content, takes up lots of screen space and is not functional. (2016-10)
css['div.sbibtd > div#sfdiv.sbibod > button.sbico-c > span.sbico._wtf._Qtf']={"*display":"none"}
css['table.gssb_c[style~="absolute;"]']={"*position":"absolute"}
for leaf in ['td','span','a','b']: css['table.gssb_c tr.gssb_i '+leaf]={"background":colour["highlight-bkg"]} # TODO: be more specific by saying gssb_c[style~="absolute;"] again ?
css['div.sbtc div.sbsb_a li.sbsb_d div']={"background":colour["highlight-bkg"]} # suggestions cursor 2015-04
css['a#logo > img[src="/images/nav_logo195.png"]']={"*display":"none"}
css['div#main div#cnt div#rcnt div.col div#ifb div.rg_meta,div#main div#cnt div#rcnt div.col div#ifb div.rg_bb_i div.rg_bb_i_meta']={"*display":"none"} # image search
css['div#mngb > div#gb > div.gb_Sb,body#gsr.srp > div#mngb']={"*display":"none"} # other graphical clutter they added 2014-09 and 2014-10
css['div#gbqfbw > button#gbqfb > span.gbqfi:empty']={'*display':'none'} # 2549-pixel high image on Android shop that messes up scrolling 2016-08
css['div.kv > cite + div.action-menu.ab_ctl > a[aria-label="Result details"]'] = {'*display':'none'} # it's supposed to just reveal the "Cached" or "Similar" options, but these should be displayed anyway with our CSS so it's a non-functional unlabelled link: save confusion
# Hack for Wikipedia/MediaWiki diffs (diffchange) and Assembla diffs (was, now) and Sourceforge (vc_, gd, gi, .diff-*) and GitHub (code-deletion, code-addition) and CGit
k = ".diffchange, .was, .now, .vc_diff_change, .vc_diff_remove, .vc_diff_add, .wDiffHtmlDelete, .wDiffHtmlInsert, pre > span.gd, pre > span.gi, .diff-chg, .diff-add, .diff-rem, table.diff-table td.blob-code-deletion span, table.diff-table td.blob-code-addition span, body > div.cgit .diff .del, body > div.cgit .diff .add"
css[k] = {"color":colour["italic"]}
printOverride[k] = {"color":"black"} # TODO: shade of grey?
css[".wDiffHtmlDelete"]={"*text-decoration":"line-through"}
css['button[aria-label="Add line comment"] > svg.octicon-plus']={"display":"none"} ; emptyLink('table.diff-table button[aria-label="Add line comment"]','C',css,printOverride,colour,False,True) # GitLab: making those buttons look like "+" just to the left of the diff's "-" and "+" is confusing
css['svg[aria-label="status_success"]'],css['svg[aria-label="status_failed"]'] = {'stroke':'green'}, {'stroke':'red'} # GitLab pipelines (otherwise can get both being yellow on white, since it seems we're not overriding the white)
css['div.web-ide-promo-popover']={'**display':'none'} # GitLab 2024: sorry but when a screen magnifier is in use, the effective window size is much smaller than what your designers imagined, and that non-dismissable IDE promotion actually blocks us from using your entire site, so let's remove it (at size=unchanged; at other sizes the layout is changed anyway)
css['button.more-actions-toggle > span.icon > svg']={'width':'1em','height':'1em'}
# and media players:
css["div.mwPlayerContainer div.play-btn span.ui-icon-play:empty:after"]={"content":r'"\21E8 Play"'}
css["div.mwPlayerContainer div.play-btn span.ui-icon-pause:empty:after"]={"content":'"Pause"'}
css['body.mediawiki div[title="Play clip"]:empty:after']={"content":'"Play clip"'}
# Hack for jqMath:
if pixelSize: css["td.fm-num-frac,td.fm-den-frac"] = {"text-align":"center"}
# Partial hack for MathJax: (I wish webmasters would use
# jqMath, which is easier on user CSS, instead)
# NB we use div.MathJax_Display here but it's expanded to inline MathJax in outCss
if pixelSize:
css["div.MathJax_Display span.mfrac,span.MathJax span.mfrac"]={"display":"inline-table","vertical-align":"middle","padding":"0.5ex"}
css["div.MathJax_Display span.mfrac > span > span,span.MathJax span.mfrac > span > span"]={"display":"table-row-group","text-align":"center"}
css["div.MathJax_Display span.mfrac > span > span:first-child,span.MathJax span.mfrac > span > span:first-child"]={"display":"table-cell","border-bottom":"thin solid"}
css["div.MathJax_Display span.mfrac > span > span + span + span,span.MathJax span.mfrac > span > span + span + span"]={"display":"none"}
css["div.MathJax_Display span.msqrt > span > span + span,span.MathJax span.msqrt > span > span + span"] = {"display":"none"}
css["div.MathJax_Display span.msqrt:before,span.MathJax span.msqrt:before"]={"content":r'"\221A("'}
css["div.MathJax_Display span.msqrt:after,span.MathJax span.msqrt:after"]={"content":'")"'}
css["div.MathJax_Display span.mtable,span.MathJax span.mtable"]={"display":"inline-table"}
css["div.MathJax_Display span.mtable span.mtd,span.MathJax span.mtable span.mtd"]={"display":"table-row-group","text-align":"center"}
for el in [""," span"," img:after"]: css["div.MathJax_Display span.msubsup > span:only-child > span:first-child + span:last-child"+el]={"color":colour["italic"]} # for now, because we don't know if the span:last-child's "vertical-align" should be "sub" or "super" in this context
css["div.MathJax_Display span.msubsup > span:only-child > span:first-child + span:not(:last-child)"]={"vertical-align":"super"} # TODO: can we do superscript + subscript as an inline-table? (but need a containing element?)
css["div.MathJax_Display span.msubsup > span:only-child > span:first-child + span + span"]={"vertical-align":"sub"}
# Following workaround is for MathJax scripts which insist on images when we have fonts (TODO: check for Unicode support?) It doesn't work in IE9 or below (and possibly some other browsers) because it relies on setting img's content="" to enable the :before/:after; to be safe I'm doing this in only Webkit and Gecko for now.
for asc in list(range(0x20,0x7f))+[0xa0,0xd7]+list(range(0x2200,0x2294)): # TODO: others?
if asc in [ord('"'),ord('\\')]: continue
k = 'div.MathJax_Display img[src="http://cdn.mathjax.org/mathjax/latest/fonts/HTML-CSS/TeX/png/Main/Regular/476/%04X.png"]' % asc
webkitGeckoScreenOverride[k]={"width":"0px",'content':'""',"vertical-align":"0px"} # (don't say display=none or that'll hide the :after as well)
if asc <= 0x7f: c = chr(asc)
else: c = r'\%04X' % asc
webkitGeckoScreenOverride[k+":after"]={"content":'"'+c+'"'}
# Hack for unedited links on WP/MediaWiki and other wikis:
css["a:link.new, a:link.new i,a:link.new b, div#main-content #main-entry div#main-article a:link.createlink"]={"color":colour["coloured"] } # (TODO use a different colour?)
printOverride["a:link.new, a:link.new i,a:link.new b, div#main-content #main-entry div#main-article a:link.createlink"]={"color":"black" } # TODO: shade of grey?
# and the navpopup extension: (also adding ul.ui-autocomplete to this, used on some sites)
css['body.mediawiki > div.navpopup,body.mediawiki .referencetooltip,body.mediawiki .rt-tooltip, ul.ui-autocomplete, body.mediawiki div.mwe-popups']={"*position":"absolute","border":"blue solid","**background":colour["background"]}
css["body.mediawiki > div.ui-dialog"]={"*position":"relative","border":"blue solid"} # some media 'popups'
css["body.mediawiki div.mwe-popups a.mwe-popups-extract"]={"text-decoration":"none","color":colour["text"]} # don't underline if they present it as a very long link
css["body.mediawiki div.mwe-popups-container footer"]={"display":"none"} # 2021-02: this popup footer doesn't show on our colour schemes anyway, and it's a setting to disable the popups taking screen space away from them, bad in large print
# and the map pins (TODO: this is still only approximate! pins tend to be a bit too far to the south-west; not sure why) :
css['body.mediawiki table tr div[style^="position:absolute"]']={"*position":"absolute","background-color":"transparent"}
css['body.mediawiki table tr div[style^="position:relative"]']={"*position":"relative","*display":"inline-block","**background":colour["background"]} # inline-block needed because the percentage positioning of the 'absolute' pin div depends on the map div's width being set to that of the map (done on-site by hard-coding, but we would have to special-case it for every possible map width; inline-block is a workaround)
css['body.mediawiki table tr div[style^="position:absolute"] div[style^="position:absolute"] + div']={"display":"none"} # or the place name would overprint the map too much; it can usually be inferred from the caption
css["body.mediawiki a.cn-full-banner-click"]={"*display":"none"} # sorry, it was too big
css['body.mediawiki div.chess-board > div[style^="position:absolute"]']={"*position":"absolute","**background":colour["background"]}
css['body.mediawiki div.chess-board > div > a.image:before, body.mediawiki div.chess-board > div > a.image:after']={"content":'""'} # overriding our [..]
css['a:link div']["*padding"]="0px" # WikiMedia some site notices (the div is set to display inline, and adding padding to inline elements can cause overprinting)
css['body.mediawiki div#mw-navigation > div#mw-panel']={'**height':'100%','**overflow-y':'auto'} # for Wikimedia at size=unchanged: otherwise, when zoomed in, may get trouble with 2 scrollbars 2020-09 (because body is fixed at height 100% but excludes mw-panel, and bottom border of body is not clear so situation is difficult to distinguish from that of a rendering-as-blank EU cookie-consent popup obscuring much of the page)
css['body.mediawiki div#vector-page-titlebar-toc']={'**display':'none'} # it gets in the way of the text on small windows
# Syntax highlighting of code on various platforms:
shl_keyword = {"color":colour["italic"]}
shl_varname = {"color":colour["bold"]}
shl_comment = {"color":colour["italic"],"opacity":"0.7"}
shl_preproc = {"color":colour["headings"]}
shl_string = {"background":colour["highlight-bkg"]}
css['body.mediawiki .mw-highlight .k'] = shl_keyword # keyword
css['pre > code > span.kwd'] = shl_keyword # StackOverflow keyword
css['body.mediawiki .mw-highlight .kt'] = shl_keyword # keyword type
css['body.mediawiki .mw-highlight .n']=shl_varname # (variable) name
css['pre > code > span.pln'] = shl_varname # StackOverflow name
css['body.mediawiki .mw-highlight .nf']=shl_varname # function name
css['body.mediawiki .mw-highlight .nt']=shl_keyword # tag name(?) (in XML etc)
css['body.mediawiki .mw-highlight .na']=shl_varname # attribute name
css['body.mediawiki .mw-highlight .cm,body.mediawiki .mw-highlight .c1,body.mediawiki .mw-highlight .c']=shl_comment # comment
css['pre > code > span.com'] = shl_comment # StackOverflow comment
css['body.mediawiki .mw-highlight .cp']=shl_preproc # preprocessor
css['body.mediawiki .mw-highlight .s']=shl_string # string
css['pre > code > span.str'] = shl_string # StackOverflow string
css['body.mediawiki .mw-highlight .se']={"background":colour["highlight-bkg"],"color":colour["bold"]} # string escape character
css['body.mediawiki .mw-highlight .cpf']=shl_string # #include parameter (treated like string in some editors)
css['body.mediawiki .mw-highlight .lineno']={"color":colour["form_disabled"]}
css['a.disabled > span.buttonText']={"color":colour["form_disabled"]}
# TODO: p = punc, o = operator; mi = integer; nv = variable name; nb; others?
css['div.highlight > pre span.c1']=shl_comment # tornadoweb etc
css['div.highlight > pre span.kn,div.highlight > pre span.k']=shl_keyword
css['div.highlight > pre span.n,div.highlight > pre span.nn']=shl_varname
css['div.highlight > pre span.s1,div.highlight > pre span.s2,div.highlight > pre span.sd,div.highlight > pre span.si']=shl_string
# TODO: p = punc (and do we differentiate sd=docstring, si=% formatter)
css['pre > code span.hljs-keyword'] = shl_keyword
css['pre > code span.hljs-built_in'] = shl_varname
css['pre > code span.hljs-string'] = shl_string
css['pre > code span.hljs-comment'] = shl_comment
css['pre > code span.hljs-number'] = shl_preproc
css['pre.code > span.com'] = shl_comment
css['pre.code > span.str'] = shl_string
css['pre.code > span.kwd'] = shl_keyword
css['pre.code > span.pln'] = shl_varname
css['pre > code span.c'] = shl_comment # GitLab issue tracker 2018-10
css['pre > code span.c1'] = shl_comment
css['pre > code span.s'] = shl_string
css['pre > code span.s2'] = shl_string
css['pre > code span.k'] = shl_keyword
css['pre > code span.n'] = shl_varname
css['pre > code span.na'] = shl_varname
css['pre > code span.syntax-keyword'] = shl_keyword # XCode docs
css['pre > code span.syntax-string'] = shl_string
css['pre > code span.syntax-comment'] = shl_comment
css['pre > code span.syntax-title'] = shl_varname
css['pre > code span.syntax-built_in'] = shl_varname
css['div.dp-highlighter > ol span.comment'] = shl_comment
css['div.dp-highlighter > ol span.string'] = shl_string
css['div.dp-highlighter > ol span.keyword'] = shl_keyword
css['div.dp-highlighter > ol span.special'] = shl_varname
# TODO: number
css['td.blob-code > span.pl-k,div.highlight > pre > span.pl-k'] = shl_keyword
css['td.blob-code > span.pl-c, td.blob-code > span.pl-c > span, div.highlight > pre > span.pl-c, div.highlight > pre > span.pl-c > span'] = shl_comment
css['td.blob-code > span.pl-v,td.blob-code > span.pl-smi, div.highlight > pre > span.pl-v, div.highlight > pre > span.pl-smi'] = shl_varname
css['td.blob-code > span.pl-s, div.highlight > pre > span.pl-s'] = shl_string
css['pre > span.enscript-comment'] = shl_comment
css['pre > span.enscript-reference'] = shl_preproc
css['pre > span.enscript-string'] = shl_string
css['pre > span.enscript-function-name'] = shl_varname
css['pre > span.enscript-keyword'] = shl_keyword
css['pre > span.enscript-type'] = shl_varname
css['pre > span.comment'] = shl_comment
css['pre > span.keyword'] = shl_keyword
css['pre > span.variable'] = shl_varname
css['pre > span.string'] = shl_string
css['pre > span.ln'] = shl_preproc
css['.FileContents .u-pre span.com'] = shl_comment
css['.FileContents .u-pre span.kwd'] = shl_keyword
css['.FileContents .u-pre span.typ'] = shl_varname
css['.FileContents .u-pre span.str'] = shl_string
css['div.diff-content .line_content span.k'] = shl_keyword # GitLab merge-requests
css['div.diff-content .line_content span.n'] = shl_varname
css['div.diff-content .line_content span.nv'] = shl_varname
css['div.diff-content .line_content span.nt'] = shl_keyword
css['div.diff-content .line_content span.ni'] = shl_preproc
css['div.diff-content .line_content span.na'] = shl_varname
css['div.diff-content .line_content span.s'] = shl_string
css['div.diff-content .line_content span.s2'] = shl_string
css['div.diff-content .line_content span.cp'] = shl_preproc
css['div.diff-content .line_content span.c1'] = shl_comment
css['div.diff-content .line_content span.c'] = shl_comment
css['div.diff-content .line_content span.cm'] = shl_comment
css['span.blob-code-inner > span.pl-ent'] = shl_varname
css['span.blob-code-inner > span.pl-kos'] = shl_preproc
css['span.blob-code-inner > span.pl-s1'] = shl_varname
css['span.blob-code-inner > span.pl-s'] = shl_string
css['span.blob-code-inner > span.pl-c'] = shl_comment
css['devsite-code span.com'] = shl_comment
css['devsite-code span.kwd'] = shl_keyword
css['devsite-code span.typ'] = shl_varname
css['devsite-code span.str'] = shl_string
css['div.code-view td.lines-code code span.nx'] = shl_varname
css['div.code-view td.lines-code code span.c1'] = shl_comment
css['div.code-view td.lines-code code span.kc'] = shl_keyword
css['div.code-view td.lines-code code span.k'] = shl_keyword
css['div.code-view td.lines-code code span.s'] = shl_string
# Hack for Vodafone UK's login 2012 (stop their mousein/mouseout events going crazy with our layout)
css["ul#MUmyAccountOptions"]={"*display":"block"}
# Hack for some authoring tools that use <FONT COLOR=..> to indicate special emphasis
css["font[color],span[style=\"color: rgb(128, 0, 0);\"],span[style=\"color:red\"],span[style=\"color:rgb(255,0,0)\"]"]={"color":colour["coloured"]}
printOverride["font[color],span[style=\"color: rgb(128, 0, 0);\"],span[style=\"color:red\"],span[style=\"color:rgb(255,0,0)\"]"]={"color":"black"} # TODO: shade of grey?
# and others that use span class="Apple-style-span"
css["span.Apple-style-span"]={"color":colour["coloured"]}
printOverride["span.Apple-style-span"]={"color":"black"} # TODO: shade of grey?
# Hack for pinyinannotator
if pixelSize:
css["div.interlinear tt"]={"display":"inline-table","line-height":"1.02","text-align":"center","padding":"0.3em"}