1<?php
2/**
3 * DokuWiki Action Plugin FontFace
4 *
5 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author     Anika Henke <anika@selfthinker.org>
7 */
8// must be run within Dokuwiki
9if(!defined('DOKU_INC')) die();
10
11if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN', DOKU_INC.'lib/plugins/');
12if(!defined('DOKU_LF')) define('DOKU_LF', "\n");
13
14require_once(DOKU_PLUGIN.'action.php');
15
16/**
17 * All DokuWiki plugins to interfere with the event system
18 * need to inherit from this class
19 */
20class action_plugin_fontface extends DokuWiki_Action_Plugin {
21
22    public function __construct() {
23        $this->fontSysDir = DOKU_INC.'lib/plugins/fontface/fonts/';
24        $this->fontDir = DOKU_BASE.'lib/plugins/fontface/fonts/';
25    }
26
27    // register hook
28    function register(Doku_Event_Handler $controller) {
29        $controller->register_hook('TPL_METAHEADER_OUTPUT','BEFORE', $this, '_addFontCode');
30    }
31
32    /**
33     * Add font code (JS and CSS) depending on chosen technique
34     *
35     * @param unknown_type $event
36     * @param unknown_type $param
37     */
38    function _addFontCode(&$event, $param) {
39
40        $technique     = $this->getConf('technique');
41        $fontFileName  = $this->getConf('fontFile');
42        $fontFileName2 = $this->getConf('fontFile2');
43        $fontName      = $this->getConf('fontName');
44        $fontName2     = $this->getConf('fontName2');
45        // config option 'elements' used to be called 'headings', fallback to old option for backwards-compatibility
46        $headings      = $this->getConf('headings');
47        $elements      = !empty($headings) ? $headings : $this->getConf('elements');
48        $elements2     = $this->getConf('elements2');
49
50        $CSSfiles = array();
51        $CSSembed = '';
52
53        // don't apply anything if no technique is chosen
54        if (empty($technique)) {
55            return false;
56        }
57
58        // incomplete sanity check
59        if (empty($fontName) && empty($fontFileName) && empty($fontName2) && empty($fontFileName2)) {
60            msg("The FontFace plugin is missing some config settings.", -1);
61            return false;
62        }
63
64        // prepare CSS and JS to embed depending on the technique
65        switch ($technique) {
66            case 'fontface':
67                $CSSembed .= $this->_getFontFaceCode($fontName, $fontFileName);
68                $CSSembed .= $this->_getFontFaceCode($fontName2, $fontFileName2);
69                break;
70
71            case 'google':
72                $CSSfiles[] = $this->_getGoogleFontPath($fontFileName);
73                $CSSfiles[] = $this->_getGoogleFontPath($fontFileName2);
74                break;
75        }
76
77        // add styles automatically if elements are given, otherwise set them through CSS as usual
78        if ( !empty($elements) && !empty($fontName) ) {
79            $CSSembed .= $elements." { font-family: '".$fontName."', ".$this->getConf('genericFamily')."; }".NL;
80        }
81        if ( !empty($elements2) && !empty($fontName2) ) {
82            $CSSembed .= $elements2." { font-family: '".$fontName2."', ".$this->getConf('genericFamily2')."; }".NL;
83        }
84
85        // include all relevant CSS files
86        if (!empty($CSSfiles)){
87            foreach($CSSfiles as $CSSfile) {
88                $event->data['link'][] = array(
89                    'type'    => 'text/css',
90                    'rel'     => 'stylesheet',
91                    'media'   => 'screen',
92                    'href'    => $CSSfile
93                );
94            }
95        }
96        // embed all relevant CSS code
97        if (!empty($CSSembed)){
98            $event->data['style'][] = array(
99                'type'    => 'text/css',
100                'media'   => 'screen',
101                '_data'   => $CSSembed
102            );
103        }
104
105    }
106
107    /**
108     * Check if file option is set and if it exists
109     *
110     * @param string $file          File to check (path to system directory)
111     * @param string $fileDisplay   File to display in error message (path to web directory)
112     * @param string $fileConfig    Name of config option
113     * @return bool                 If file is okay
114     */
115    function _isFileOk($file, $fileDisplay, $fileConfig) {
116        if (empty($file)) {
117            msg("The '<strong>".$fileConfig."</strong>' config setting is <strong>not set</strong>.", -1);
118            return false;
119        } else if (!file_exists($file)) {
120            msg("The file <strong>".$fileDisplay."</strong> (".$fileConfig.") <strong>does not exist</strong>.", -1);
121            return false;
122        }
123        return true;
124    }
125
126    /**
127     * Get code to embed local uploaded font via @font-face
128     *
129     * @param string $fontName      Name of the font as used in CSS
130     * @param string $fontFileName  File name of the font without extension
131     * @return mixed                String of CSS to embed or Bool if there is nothing to embed
132     */
133    function _getFontFaceCode($fontName, $fontFileName) {
134        if (empty($fontName) || empty($fontFileName)) {
135            return false;
136        }
137
138        $fontSysDir = $this->fontSysDir;
139        $fontDir    = $this->fontDir;
140        $fontEOT    = $fontFileName.'.eot';
141        $fontWOFF   = $fontFileName.'.woff';
142        $fontWOFF2  = $fontFileName.'.woff2';
143        $fontTTF    = $fontFileName.'.ttf';
144        $fontSVG    = $fontFileName.'.svg';
145
146        // check if at least ttf and woff files exist
147        if (!$this->_isFileOk($fontSysDir.$fontWOFF, $fontDir.$fontWOFF, 'fontFile') ||
148            !$this->_isFileOk($fontSysDir.$fontTTF,  $fontDir.$fontTTF,  'fontFile')) {
149            return false;
150        }
151
152        $CSSembed       = "@font-face {".NL.
153                          "  font-family: '".$fontName."';".NL;
154        if (file_exists($fontSysDir.$fontEOT)) {
155            $CSSembed   .= "  src: url('".$fontDir.$fontEOT."');".NL;
156        }
157        $CSSembed       .= "  src: ";
158        if (file_exists($fontSysDir.$fontEOT)) {
159            $CSSembed   .= "       url('".$fontDir.$fontEOT."?#iefix') format('embedded-opentype'),".NL;
160        }
161        if (file_exists($fontSysDir.$fontWOFF2)) {
162            $CSSembed   .= "       url('".$fontDir.$fontWOFF2."') format('woff2'),".NL;
163        }
164        $CSSembed       .= "       url('".$fontDir.$fontWOFF."') format('woff'),".NL;
165        $CSSembed       .= "       url('".$fontDir.$fontTTF."') format('truetype')";
166        if (file_exists($fontSysDir.$fontSVG)) {
167            $CSSembed   .= ",".NL;
168            $CSSembed   .= "       url('".$fontDir.$fontSVG."#".str_replace(' ', '', $fontName)."') format('svg')";
169        }
170        $CSSembed       .= ";".NL.
171                           "  font-weight: normal;".NL.
172                           "  font-style: normal;".NL.
173                           "}".NL;
174        return $CSSembed;
175    }
176
177    /**
178     * Get path to Google font
179     *
180     * @param string $fontFileName  File name of the font without extension
181     * @return mixed                String of CSS file path to embed or Bool if there is nothing to embed
182     */
183    function _getGoogleFontPath($fontFileName) {
184        // check if required option is set
185        if (empty($fontFileName)) {
186            return false;
187        }
188        return 'https://fonts.googleapis.com/css?family='.str_replace(' ', '+', $fontFileName);
189    }
190
191}
192
193// vim:ts=4:sw=4:
194