asciidoctor-epub3 1.0.0.alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.adoc +22 -0
- data/NOTICE.adoc +53 -0
- data/README.adoc +744 -0
- data/Rakefile +78 -0
- data/bin/adb-push-ebook +25 -0
- data/bin/asciidoctor-epub3 +15 -0
- data/data/fonts/assorted-icons.ttf +0 -0
- data/data/fonts/fontawesome-icons.ttf +0 -0
- data/data/fonts/mplus1mn-bold-ascii.ttf +0 -0
- data/data/fonts/mplus1mn-bolditalic-ascii.ttf +0 -0
- data/data/fonts/mplus1mn-italic-ascii.ttf +0 -0
- data/data/fonts/mplus1mn-regular-ascii-conums.ttf +0 -0
- data/data/fonts/mplus1p-bold-latin-cyrillic.ttf +0 -0
- data/data/fonts/mplus1p-bold-latin-ext.ttf +0 -0
- data/data/fonts/mplus1p-bold-latin.ttf +0 -0
- data/data/fonts/mplus1p-bold-multilingual.ttf +0 -0
- data/data/fonts/mplus1p-light-latin-cyrillic.ttf +0 -0
- data/data/fonts/mplus1p-light-latin-ext.ttf +0 -0
- data/data/fonts/mplus1p-light-latin.ttf +0 -0
- data/data/fonts/mplus1p-light-multilingual.ttf +0 -0
- data/data/fonts/mplus1p-regular-latin-cyrillic.ttf +0 -0
- data/data/fonts/mplus1p-regular-latin-ext.ttf +0 -0
- data/data/fonts/mplus1p-regular-latin.ttf +0 -0
- data/data/fonts/mplus1p-regular-multilingual.ttf +0 -0
- data/data/fonts/notoserif-bold-latin-cyrillic.ttf +0 -0
- data/data/fonts/notoserif-bold-latin-ext.ttf +0 -0
- data/data/fonts/notoserif-bold-latin.ttf +0 -0
- data/data/fonts/notoserif-bold-multilingual.ttf +0 -0
- data/data/fonts/notoserif-bolditalic-latin-cyrillic.ttf +0 -0
- data/data/fonts/notoserif-bolditalic-latin-ext.ttf +0 -0
- data/data/fonts/notoserif-bolditalic-latin.ttf +0 -0
- data/data/fonts/notoserif-bolditalic-multilingual.ttf +0 -0
- data/data/fonts/notoserif-italic-latin-cyrillic.ttf +0 -0
- data/data/fonts/notoserif-italic-latin-ext.ttf +0 -0
- data/data/fonts/notoserif-italic-latin.ttf +0 -0
- data/data/fonts/notoserif-italic-multilingual.ttf +0 -0
- data/data/fonts/notoserif-regular-latin-cyrillic.ttf +0 -0
- data/data/fonts/notoserif-regular-latin-ext.ttf +0 -0
- data/data/fonts/notoserif-regular-latin.ttf +0 -0
- data/data/fonts/notoserif-regular-multilingual.ttf +0 -0
- data/data/images/default-avatar.jpg +0 -0
- data/data/images/default-avatar.png +0 -0
- data/data/images/default-avatar.svg +67 -0
- data/data/images/default-cover-large.png +0 -0
- data/data/images/default-cover.png +0 -0
- data/data/images/default-cover.svg +53 -0
- data/data/images/default-headshot.jpg +0 -0
- data/data/images/default-headshot.png +0 -0
- data/data/samples/asciidoctor-epub3-readme.adoc +744 -0
- data/data/samples/asciidoctor-js-extension.adoc +46 -0
- data/data/samples/asciidoctor-js-introduction.adoc +91 -0
- data/data/samples/i18n.adoc +161 -0
- data/data/samples/images/asciidoctor-js-chrome-extension.png +0 -0
- data/data/samples/images/avatars/graphitefriction.png +0 -0
- data/data/samples/images/avatars/mogztter.png +0 -0
- data/data/samples/images/avatars/mojavelinux.png +0 -0
- data/data/samples/images/correct-text-justification.png +0 -0
- data/data/samples/images/incorrect-text-justification.png +0 -0
- data/data/samples/images/screenshots/chapter-title-day.png +0 -0
- data/data/samples/images/screenshots/chapter-title.png +0 -0
- data/data/samples/images/screenshots/figure-admonition.png +0 -0
- data/data/samples/images/screenshots/section-title-paragraph.png +0 -0
- data/data/samples/images/screenshots/sidebar.png +0 -0
- data/data/samples/images/screenshots/table.png +0 -0
- data/data/samples/images/screenshots/text.png +0 -0
- data/data/samples/sample-book.adoc +20 -0
- data/data/samples/sample-content.adoc +168 -0
- data/data/styles/color-palette.css +28 -0
- data/data/styles/epub3-css3-only.css +161 -0
- data/data/styles/epub3-fonts.css +94 -0
- data/data/styles/epub3.css +1293 -0
- data/lib/asciidoctor-epub3.rb +5 -0
- data/lib/asciidoctor-epub3/converter.rb +859 -0
- data/lib/asciidoctor-epub3/core_ext/string.rb +7 -0
- data/lib/asciidoctor-epub3/font_icon_map.rb +376 -0
- data/lib/asciidoctor-epub3/packager.rb +466 -0
- data/lib/asciidoctor-epub3/spine_item_processor.rb +72 -0
- data/lib/asciidoctor-epub3/version.rb +5 -0
- data/scripts/generate-font-subsets.pe +225 -0
- metadata +192 -0
| @@ -0,0 +1,376 @@ | |
| 1 | 
            +
            module Asciidoctor
         | 
| 2 | 
            +
            module Epub3
         | 
| 3 | 
            +
              # Map of Font Awesome icon names to unicode characters 
         | 
| 4 | 
            +
              FontIconMap = {
         | 
| 5 | 
            +
                glass: '\f000',
         | 
| 6 | 
            +
                music: '\f001',
         | 
| 7 | 
            +
                search: '\f002',
         | 
| 8 | 
            +
                envelope_o: '\f003',
         | 
| 9 | 
            +
                heart: '\f004',
         | 
| 10 | 
            +
                star: '\f005',
         | 
| 11 | 
            +
                star_o: '\f006',
         | 
| 12 | 
            +
                user: '\f007',
         | 
| 13 | 
            +
                film: '\f008',
         | 
| 14 | 
            +
                th_large: '\f009',
         | 
| 15 | 
            +
                th: '\f00a',
         | 
| 16 | 
            +
                th_list: '\f00b',
         | 
| 17 | 
            +
                check: '\f00c',
         | 
| 18 | 
            +
                times: '\f00d',
         | 
| 19 | 
            +
                search_plus: '\f00e',
         | 
| 20 | 
            +
                search_minus: '\f010',
         | 
| 21 | 
            +
                power_off: '\f011',
         | 
| 22 | 
            +
                signal: '\f012',
         | 
| 23 | 
            +
                cog: '\f013',
         | 
| 24 | 
            +
                trash_o: '\f014',
         | 
| 25 | 
            +
                home: '\f015',
         | 
| 26 | 
            +
                file_o: '\f016',
         | 
| 27 | 
            +
                clock_o: '\f017',
         | 
| 28 | 
            +
                road: '\f018',
         | 
| 29 | 
            +
                download: '\f019',
         | 
| 30 | 
            +
                arrow_circle_o_down: '\f01a',
         | 
| 31 | 
            +
                arrow_circle_o_up: '\f01b',
         | 
| 32 | 
            +
                inbox: '\f01c',
         | 
| 33 | 
            +
                play_circle_o: '\f01d',
         | 
| 34 | 
            +
                repeat: '\f01e',
         | 
| 35 | 
            +
                refresh: '\f021',
         | 
| 36 | 
            +
                list_alt: '\f022',
         | 
| 37 | 
            +
                lock: '\f023',
         | 
| 38 | 
            +
                flag: '\f024',
         | 
| 39 | 
            +
                headphones: '\f025',
         | 
| 40 | 
            +
                volume_off: '\f026',
         | 
| 41 | 
            +
                volume_down: '\f027',
         | 
| 42 | 
            +
                volume_up: '\f028',
         | 
| 43 | 
            +
                qrcode: '\f029',
         | 
| 44 | 
            +
                barcode: '\f02a',
         | 
| 45 | 
            +
                tag: '\f02b',
         | 
| 46 | 
            +
                tags: '\f02c',
         | 
| 47 | 
            +
                book: '\f02d',
         | 
| 48 | 
            +
                bookmark: '\f02e',
         | 
| 49 | 
            +
                print: '\f02f',
         | 
| 50 | 
            +
                camera: '\f030',
         | 
| 51 | 
            +
                font: '\f031',
         | 
| 52 | 
            +
                bold: '\f032',
         | 
| 53 | 
            +
                italic: '\f033',
         | 
| 54 | 
            +
                text_height: '\f034',
         | 
| 55 | 
            +
                text_width: '\f035',
         | 
| 56 | 
            +
                align_left: '\f036',
         | 
| 57 | 
            +
                align_center: '\f037',
         | 
| 58 | 
            +
                align_right: '\f038',
         | 
| 59 | 
            +
                align_justify: '\f039',
         | 
| 60 | 
            +
                list: '\f03a',
         | 
| 61 | 
            +
                outdent: '\f03b',
         | 
| 62 | 
            +
                indent: '\f03c',
         | 
| 63 | 
            +
                video_camera: '\f03d',
         | 
| 64 | 
            +
                picture_o: '\f03e',
         | 
| 65 | 
            +
                pencil: '\f040',
         | 
| 66 | 
            +
                map_marker: '\f041',
         | 
| 67 | 
            +
                adjust: '\f042',
         | 
| 68 | 
            +
                tint: '\f043',
         | 
| 69 | 
            +
                pencil_square_o: '\f044',
         | 
| 70 | 
            +
                share_square_o: '\f045',
         | 
| 71 | 
            +
                check_square_o: '\f046',
         | 
| 72 | 
            +
                arrows: '\f047',
         | 
| 73 | 
            +
                step_backward: '\f048',
         | 
| 74 | 
            +
                fast_backward: '\f049',
         | 
| 75 | 
            +
                backward: '\f04a',
         | 
| 76 | 
            +
                play: '\f04b',
         | 
| 77 | 
            +
                pause: '\f04c',
         | 
| 78 | 
            +
                stop: '\f04d',
         | 
| 79 | 
            +
                forward: '\f04e',
         | 
| 80 | 
            +
                fast_forward: '\f050',
         | 
| 81 | 
            +
                step_forward: '\f051',
         | 
| 82 | 
            +
                eject: '\f052',
         | 
| 83 | 
            +
                chevron_left: '\f053',
         | 
| 84 | 
            +
                chevron_right: '\f054',
         | 
| 85 | 
            +
                plus_circle: '\f055',
         | 
| 86 | 
            +
                minus_circle: '\f056',
         | 
| 87 | 
            +
                times_circle: '\f057',
         | 
| 88 | 
            +
                check_circle: '\f058',
         | 
| 89 | 
            +
                question_circle: '\f059',
         | 
| 90 | 
            +
                info_circle: '\f05a',
         | 
| 91 | 
            +
                crosshairs: '\f05b',
         | 
| 92 | 
            +
                times_circle_o: '\f05c',
         | 
| 93 | 
            +
                check_circle_o: '\f05d',
         | 
| 94 | 
            +
                ban: '\f05e',
         | 
| 95 | 
            +
                arrow_left: '\f060',
         | 
| 96 | 
            +
                arrow_right: '\f061',
         | 
| 97 | 
            +
                arrow_up: '\f062',
         | 
| 98 | 
            +
                arrow_down: '\f063',
         | 
| 99 | 
            +
                share: '\f064',
         | 
| 100 | 
            +
                expand: '\f065',
         | 
| 101 | 
            +
                compress: '\f066',
         | 
| 102 | 
            +
                plus: '\f067',
         | 
| 103 | 
            +
                minus: '\f068',
         | 
| 104 | 
            +
                asterisk: '\f069',
         | 
| 105 | 
            +
                exclamation_circle: '\f06a',
         | 
| 106 | 
            +
                gift: '\f06b',
         | 
| 107 | 
            +
                leaf: '\f06c',
         | 
| 108 | 
            +
                fire: '\f06d',
         | 
| 109 | 
            +
                eye: '\f06e',
         | 
| 110 | 
            +
                eye_slash: '\f070',
         | 
| 111 | 
            +
                exclamation_triangle: '\f071',
         | 
| 112 | 
            +
                plane: '\f072',
         | 
| 113 | 
            +
                calendar: '\f073',
         | 
| 114 | 
            +
                random: '\f074',
         | 
| 115 | 
            +
                comment: '\f075',
         | 
| 116 | 
            +
                magnet: '\f076',
         | 
| 117 | 
            +
                chevron_up: '\f077',
         | 
| 118 | 
            +
                chevron_down: '\f078',
         | 
| 119 | 
            +
                retweet: '\f079',
         | 
| 120 | 
            +
                shopping_cart: '\f07a',
         | 
| 121 | 
            +
                folder: '\f07b',
         | 
| 122 | 
            +
                folder_open: '\f07c',
         | 
| 123 | 
            +
                arrows_v: '\f07d',
         | 
| 124 | 
            +
                arrows_h: '\f07e',
         | 
| 125 | 
            +
                bar_chart_o: '\f080',
         | 
| 126 | 
            +
                twitter_square: '\f081',
         | 
| 127 | 
            +
                facebook_square: '\f082',
         | 
| 128 | 
            +
                camera_retro: '\f083',
         | 
| 129 | 
            +
                key: '\f084',
         | 
| 130 | 
            +
                cogs: '\f085',
         | 
| 131 | 
            +
                comments: '\f086',
         | 
| 132 | 
            +
                thumbs_o_up: '\f087',
         | 
| 133 | 
            +
                thumbs_o_down: '\f088',
         | 
| 134 | 
            +
                star_half: '\f089',
         | 
| 135 | 
            +
                heart_o: '\f08a',
         | 
| 136 | 
            +
                sign_out: '\f08b',
         | 
| 137 | 
            +
                linkedin_square: '\f08c',
         | 
| 138 | 
            +
                thumb_tack: '\f08d',
         | 
| 139 | 
            +
                external_link: '\f08e',
         | 
| 140 | 
            +
                sign_in: '\f090',
         | 
| 141 | 
            +
                trophy: '\f091',
         | 
| 142 | 
            +
                github_square: '\f092',
         | 
| 143 | 
            +
                upload: '\f093',
         | 
| 144 | 
            +
                lemon_o: '\f094',
         | 
| 145 | 
            +
                phone: '\f095',
         | 
| 146 | 
            +
                square_o: '\f096',
         | 
| 147 | 
            +
                bookmark_o: '\f097',
         | 
| 148 | 
            +
                phone_square: '\f098',
         | 
| 149 | 
            +
                twitter: '\f099',
         | 
| 150 | 
            +
                facebook: '\f09a',
         | 
| 151 | 
            +
                github: '\f09b',
         | 
| 152 | 
            +
                unlock: '\f09c',
         | 
| 153 | 
            +
                credit_card: '\f09d',
         | 
| 154 | 
            +
                rss: '\f09e',
         | 
| 155 | 
            +
                hdd_o: '\f0a0',
         | 
| 156 | 
            +
                bullhorn: '\f0a1',
         | 
| 157 | 
            +
                bell: '\f0f3',
         | 
| 158 | 
            +
                certificate: '\f0a3',
         | 
| 159 | 
            +
                hand_o_right: '\f0a4',
         | 
| 160 | 
            +
                hand_o_left: '\f0a5',
         | 
| 161 | 
            +
                hand_o_up: '\f0a6',
         | 
| 162 | 
            +
                hand_o_down: '\f0a7',
         | 
| 163 | 
            +
                arrow_circle_left: '\f0a8',
         | 
| 164 | 
            +
                arrow_circle_right: '\f0a9',
         | 
| 165 | 
            +
                arrow_circle_up: '\f0aa',
         | 
| 166 | 
            +
                arrow_circle_down: '\f0ab',
         | 
| 167 | 
            +
                globe: '\f0ac',
         | 
| 168 | 
            +
                wrench: '\f0ad',
         | 
| 169 | 
            +
                tasks: '\f0ae',
         | 
| 170 | 
            +
                filter: '\f0b0',
         | 
| 171 | 
            +
                briefcase: '\f0b1',
         | 
| 172 | 
            +
                arrows_alt: '\f0b2',
         | 
| 173 | 
            +
                users: '\f0c0',
         | 
| 174 | 
            +
                link: '\f0c1',
         | 
| 175 | 
            +
                cloud: '\f0c2',
         | 
| 176 | 
            +
                flask: '\f0c3',
         | 
| 177 | 
            +
                scissors: '\f0c4',
         | 
| 178 | 
            +
                files_o: '\f0c5',
         | 
| 179 | 
            +
                paperclip: '\f0c6',
         | 
| 180 | 
            +
                floppy_o: '\f0c7',
         | 
| 181 | 
            +
                square: '\f0c8',
         | 
| 182 | 
            +
                bars: '\f0c9',
         | 
| 183 | 
            +
                list_ul: '\f0ca',
         | 
| 184 | 
            +
                list_ol: '\f0cb',
         | 
| 185 | 
            +
                strikethrough: '\f0cc',
         | 
| 186 | 
            +
                underline: '\f0cd',
         | 
| 187 | 
            +
                table: '\f0ce',
         | 
| 188 | 
            +
                magic: '\f0d0',
         | 
| 189 | 
            +
                truck: '\f0d1',
         | 
| 190 | 
            +
                pinterest: '\f0d2',
         | 
| 191 | 
            +
                pinterest_square: '\f0d3',
         | 
| 192 | 
            +
                google_plus_square: '\f0d4',
         | 
| 193 | 
            +
                google_plus: '\f0d5',
         | 
| 194 | 
            +
                money: '\f0d6',
         | 
| 195 | 
            +
                caret_down: '\f0d7',
         | 
| 196 | 
            +
                caret_up: '\f0d8',
         | 
| 197 | 
            +
                caret_left: '\f0d9',
         | 
| 198 | 
            +
                caret_right: '\f0da',
         | 
| 199 | 
            +
                columns: '\f0db',
         | 
| 200 | 
            +
                sort: '\f0dc',
         | 
| 201 | 
            +
                sort_asc: '\f0dd',
         | 
| 202 | 
            +
                sort_desc: '\f0de',
         | 
| 203 | 
            +
                envelope: '\f0e0',
         | 
| 204 | 
            +
                linkedin: '\f0e1',
         | 
| 205 | 
            +
                undo: '\f0e2',
         | 
| 206 | 
            +
                gavel: '\f0e3',
         | 
| 207 | 
            +
                tachometer: '\f0e4',
         | 
| 208 | 
            +
                comment_o: '\f0e5',
         | 
| 209 | 
            +
                comments_o: '\f0e6',
         | 
| 210 | 
            +
                bolt: '\f0e7',
         | 
| 211 | 
            +
                sitemap: '\f0e8',
         | 
| 212 | 
            +
                umbrella: '\f0e9',
         | 
| 213 | 
            +
                clipboard: '\f0ea',
         | 
| 214 | 
            +
                lightbulb_o: '\f0eb',
         | 
| 215 | 
            +
                exchange: '\f0ec',
         | 
| 216 | 
            +
                cloud_download: '\f0ed',
         | 
| 217 | 
            +
                cloud_upload: '\f0ee',
         | 
| 218 | 
            +
                user_md: '\f0f0',
         | 
| 219 | 
            +
                stethoscope: '\f0f1',
         | 
| 220 | 
            +
                suitcase: '\f0f2',
         | 
| 221 | 
            +
                bell_o: '\f0a2',
         | 
| 222 | 
            +
                coffee: '\f0f4',
         | 
| 223 | 
            +
                cutlery: '\f0f5',
         | 
| 224 | 
            +
                file_text_o: '\f0f6',
         | 
| 225 | 
            +
                building_o: '\f0f7',
         | 
| 226 | 
            +
                hospital_o: '\f0f8',
         | 
| 227 | 
            +
                ambulance: '\f0f9',
         | 
| 228 | 
            +
                medkit: '\f0fa',
         | 
| 229 | 
            +
                fighter_jet: '\f0fb',
         | 
| 230 | 
            +
                beer: '\f0fc',
         | 
| 231 | 
            +
                h_square: '\f0fd',
         | 
| 232 | 
            +
                plus_square: '\f0fe',
         | 
| 233 | 
            +
                angle_double_left: '\f100',
         | 
| 234 | 
            +
                angle_double_right: '\f101',
         | 
| 235 | 
            +
                angle_double_up: '\f102',
         | 
| 236 | 
            +
                angle_double_down: '\f103',
         | 
| 237 | 
            +
                angle_left: '\f104',
         | 
| 238 | 
            +
                angle_right: '\f105',
         | 
| 239 | 
            +
                angle_up: '\f106',
         | 
| 240 | 
            +
                angle_down: '\f107',
         | 
| 241 | 
            +
                desktop: '\f108',
         | 
| 242 | 
            +
                laptop: '\f109',
         | 
| 243 | 
            +
                tablet: '\f10a',
         | 
| 244 | 
            +
                mobile: '\f10b',
         | 
| 245 | 
            +
                circle_o: '\f10c',
         | 
| 246 | 
            +
                quote_left: '\f10d',
         | 
| 247 | 
            +
                quote_right: '\f10e',
         | 
| 248 | 
            +
                spinner: '\f110',
         | 
| 249 | 
            +
                circle: '\f111',
         | 
| 250 | 
            +
                reply: '\f112',
         | 
| 251 | 
            +
                github_alt: '\f113',
         | 
| 252 | 
            +
                folder_o: '\f114',
         | 
| 253 | 
            +
                folder_open_o: '\f115',
         | 
| 254 | 
            +
                smile_o: '\f118',
         | 
| 255 | 
            +
                frown_o: '\f119',
         | 
| 256 | 
            +
                meh_o: '\f11a',
         | 
| 257 | 
            +
                gamepad: '\f11b',
         | 
| 258 | 
            +
                keyboard_o: '\f11c',
         | 
| 259 | 
            +
                flag_o: '\f11d',
         | 
| 260 | 
            +
                flag_checkered: '\f11e',
         | 
| 261 | 
            +
                terminal: '\f120',
         | 
| 262 | 
            +
                code: '\f121',
         | 
| 263 | 
            +
                reply_all: '\f122',
         | 
| 264 | 
            +
                mail_reply_all: '\f122',
         | 
| 265 | 
            +
                star_half_o: '\f123',
         | 
| 266 | 
            +
                location_arrow: '\f124',
         | 
| 267 | 
            +
                crop: '\f125',
         | 
| 268 | 
            +
                code_fork: '\f126',
         | 
| 269 | 
            +
                chain_broken: '\f127',
         | 
| 270 | 
            +
                question: '\f128',
         | 
| 271 | 
            +
                info: '\f129',
         | 
| 272 | 
            +
                exclamation: '\f12a',
         | 
| 273 | 
            +
                superscript: '\f12b',
         | 
| 274 | 
            +
                subscript: '\f12c',
         | 
| 275 | 
            +
                eraser: '\f12d',
         | 
| 276 | 
            +
                puzzle_piece: '\f12e',
         | 
| 277 | 
            +
                microphone: '\f130',
         | 
| 278 | 
            +
                microphone_slash: '\f131',
         | 
| 279 | 
            +
                shield: '\f132',
         | 
| 280 | 
            +
                calendar_o: '\f133',
         | 
| 281 | 
            +
                fire_extinguisher: '\f134',
         | 
| 282 | 
            +
                rocket: '\f135',
         | 
| 283 | 
            +
                maxcdn: '\f136',
         | 
| 284 | 
            +
                chevron_circle_left: '\f137',
         | 
| 285 | 
            +
                chevron_circle_right: '\f138',
         | 
| 286 | 
            +
                chevron_circle_up: '\f139',
         | 
| 287 | 
            +
                chevron_circle_down: '\f13a',
         | 
| 288 | 
            +
                html5: '\f13b',
         | 
| 289 | 
            +
                css3: '\f13c',
         | 
| 290 | 
            +
                anchor: '\f13d',
         | 
| 291 | 
            +
                unlock_alt: '\f13e',
         | 
| 292 | 
            +
                bullseye: '\f140',
         | 
| 293 | 
            +
                ellipsis_h: '\f141',
         | 
| 294 | 
            +
                ellipsis_v: '\f142',
         | 
| 295 | 
            +
                rss_square: '\f143',
         | 
| 296 | 
            +
                play_circle: '\f144',
         | 
| 297 | 
            +
                ticket: '\f145',
         | 
| 298 | 
            +
                minus_square: '\f146',
         | 
| 299 | 
            +
                minus_square_o: '\f147',
         | 
| 300 | 
            +
                level_up: '\f148',
         | 
| 301 | 
            +
                level_down: '\f149',
         | 
| 302 | 
            +
                check_square: '\f14a',
         | 
| 303 | 
            +
                pencil_square: '\f14b',
         | 
| 304 | 
            +
                external_link_square: '\f14c',
         | 
| 305 | 
            +
                share_square: '\f14d',
         | 
| 306 | 
            +
                compass: '\f14e',
         | 
| 307 | 
            +
                caret_square_o_down: '\f150',
         | 
| 308 | 
            +
                caret_square_o_up: '\f151',
         | 
| 309 | 
            +
                caret_square_o_right: '\f152',
         | 
| 310 | 
            +
                eur: '\f153',
         | 
| 311 | 
            +
                gbp: '\f154',
         | 
| 312 | 
            +
                usd: '\f155',
         | 
| 313 | 
            +
                inr: '\f156',
         | 
| 314 | 
            +
                jpy: '\f157',
         | 
| 315 | 
            +
                rub: '\f158',
         | 
| 316 | 
            +
                krw: '\f159',
         | 
| 317 | 
            +
                btc: '\f15a',
         | 
| 318 | 
            +
                file: '\f15b',
         | 
| 319 | 
            +
                file_text: '\f15c',
         | 
| 320 | 
            +
                sort_alpha_asc: '\f15d',
         | 
| 321 | 
            +
                sort_alpha_desc: '\f15e',
         | 
| 322 | 
            +
                sort_amount_asc: '\f160',
         | 
| 323 | 
            +
                sort_amount_desc: '\f161',
         | 
| 324 | 
            +
                sort_numeric_asc: '\f162',
         | 
| 325 | 
            +
                sort_numeric_desc: '\f163',
         | 
| 326 | 
            +
                thumbs_up: '\f164',
         | 
| 327 | 
            +
                thumbs_down: '\f165',
         | 
| 328 | 
            +
                youtube_square: '\f166',
         | 
| 329 | 
            +
                youtube: '\f167',
         | 
| 330 | 
            +
                xing: '\f168',
         | 
| 331 | 
            +
                xing_square: '\f169',
         | 
| 332 | 
            +
                youtube_play: '\f16a',
         | 
| 333 | 
            +
                dropbox: '\f16b',
         | 
| 334 | 
            +
                stack_overflow: '\f16c',
         | 
| 335 | 
            +
                instagram: '\f16d',
         | 
| 336 | 
            +
                flickr: '\f16e',
         | 
| 337 | 
            +
                adn: '\f170',
         | 
| 338 | 
            +
                bitbucket: '\f171',
         | 
| 339 | 
            +
                bitbucket_square: '\f172',
         | 
| 340 | 
            +
                tumblr: '\f173',
         | 
| 341 | 
            +
                tumblr_square: '\f174',
         | 
| 342 | 
            +
                long_arrow_down: '\f175',
         | 
| 343 | 
            +
                long_arrow_up: '\f176',
         | 
| 344 | 
            +
                long_arrow_left: '\f177',
         | 
| 345 | 
            +
                long_arrow_right: '\f178',
         | 
| 346 | 
            +
                apple: '\f179',
         | 
| 347 | 
            +
                windows: '\f17a',
         | 
| 348 | 
            +
                android: '\f17b',
         | 
| 349 | 
            +
                linux: '\f17c',
         | 
| 350 | 
            +
                dribbble: '\f17d',
         | 
| 351 | 
            +
                skype: '\f17e',
         | 
| 352 | 
            +
                foursquare: '\f180',
         | 
| 353 | 
            +
                trello: '\f181',
         | 
| 354 | 
            +
                female: '\f182',
         | 
| 355 | 
            +
                male: '\f183',
         | 
| 356 | 
            +
                gittip: '\f184',
         | 
| 357 | 
            +
                sun_o: '\f185',
         | 
| 358 | 
            +
                moon_o: '\f186',
         | 
| 359 | 
            +
                archive: '\f187',
         | 
| 360 | 
            +
                bug: '\f188',
         | 
| 361 | 
            +
                vk: '\f189',
         | 
| 362 | 
            +
                weibo: '\f18a',
         | 
| 363 | 
            +
                renren: '\f18b',
         | 
| 364 | 
            +
                pagelines: '\f18c',
         | 
| 365 | 
            +
                stack_exchange: '\f18d',
         | 
| 366 | 
            +
                arrow_circle_o_right: '\f18e',
         | 
| 367 | 
            +
                arrow_circle_o_left: '\f190',
         | 
| 368 | 
            +
                caret_square_o_left: '\f191',
         | 
| 369 | 
            +
                dot_circle_o: '\f192',
         | 
| 370 | 
            +
                wheelchair: '\f193',
         | 
| 371 | 
            +
                vimeo_square: '\f194',
         | 
| 372 | 
            +
                try: '\f195',
         | 
| 373 | 
            +
                plus_square_o: '\f196'
         | 
| 374 | 
            +
              }
         | 
| 375 | 
            +
            end
         | 
| 376 | 
            +
            end
         | 
| @@ -0,0 +1,466 @@ | |
| 1 | 
            +
            require_relative 'core_ext/string'
         | 
| 2 | 
            +
            autoload :FileUtils, 'fileutils'
         | 
| 3 | 
            +
            autoload :Open3, 'open3'
         | 
| 4 | 
            +
            autoload :Shellwords, 'shellwords'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module Asciidoctor
         | 
| 7 | 
            +
            module Epub3
         | 
| 8 | 
            +
            module GepubBuilderMixin
         | 
| 9 | 
            +
              DATA_DIR = ::File.expand_path(::File.join ::File.dirname(__FILE__), '..', '..', 'data')
         | 
| 10 | 
            +
              SAMPLES_DIR = ::File.join DATA_DIR, 'samples'
         | 
| 11 | 
            +
              WordJoiner = Epub3::WordJoiner
         | 
| 12 | 
            +
              FromHtmlSpecialCharsMap = ContentConverter::FromHtmlSpecialCharsMap
         | 
| 13 | 
            +
              FromHtmlSpecialCharsRx = ContentConverter::FromHtmlSpecialCharsRx
         | 
| 14 | 
            +
              CsvDelimiterRx = /\s*,\s*/
         | 
| 15 | 
            +
              DefaultCoverImage = 'images/default-cover.png'
         | 
| 16 | 
            +
              InlineImageMacroRx = /^image:(.*?)\[(.*?)\]$/
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              def sanitized_doctitle doc, target = :plain
         | 
| 19 | 
            +
                return (doc.attr 'untitled-label') unless doc.header?
         | 
| 20 | 
            +
                title = case target
         | 
| 21 | 
            +
                when :attribute_cdata
         | 
| 22 | 
            +
                  doc.doctitle(sanitize: true).gsub('"', '"')
         | 
| 23 | 
            +
                when :element_cdata
         | 
| 24 | 
            +
                  doc.doctitle sanitize: true
         | 
| 25 | 
            +
                when :pcdata
         | 
| 26 | 
            +
                  doc.doctitle
         | 
| 27 | 
            +
                when :plain
         | 
| 28 | 
            +
                  doc.doctitle(sanitize: true).gsub(FromHtmlSpecialCharsRx, FromHtmlSpecialCharsMap)
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
                title.gsub WordJoiner, ''
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
              def add_theme_assets doc
         | 
| 34 | 
            +
                builder = self
         | 
| 35 | 
            +
                format = @format
         | 
| 36 | 
            +
                workdir = if doc.attr? 'epub3-stylesdir'
         | 
| 37 | 
            +
                  ::File.join doc.attr('docdir'), doc.attr('epub3-stylesdir')
         | 
| 38 | 
            +
                else
         | 
| 39 | 
            +
                  ::File.join DATA_DIR, 'styles'
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                # TODO improve design/UX of custom theme functionality, including custom fonts
         | 
| 43 | 
            +
                resources workdir: workdir do
         | 
| 44 | 
            +
                  file 'styles/epub3.css' => (builder.postprocess_css_file 'epub3.css', format)
         | 
| 45 | 
            +
                  file 'styles/epub3-css3-only.css' => (builder.postprocess_css_file 'epub3-css3-only.css', format)
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                resources workdir: DATA_DIR do
         | 
| 49 | 
            +
                  #file 'styles/epub3.css' => (builder.postprocess_css_file 'styles/epub3.css', format)
         | 
| 50 | 
            +
                  #file 'styles/epub3-css3-only.css' => (builder.postprocess_css_file 'styles/epub3-css3-only.css', format)
         | 
| 51 | 
            +
                  font_list, font_css = builder.select_fonts 'styles/epub3-fonts.css', (doc.attr 'scripts', 'latin')
         | 
| 52 | 
            +
                  file 'styles/epub3-fonts.css' => font_css
         | 
| 53 | 
            +
                  with_media_type 'application/x-font-ttf' do
         | 
| 54 | 
            +
                    files(*font_list)
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
              end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
              def add_cover_image doc
         | 
| 60 | 
            +
                imagesdir = (doc.attr 'imagesdir', '.').chomp '/'
         | 
| 61 | 
            +
                imagesdir = (imagesdir == '.' ? nil : %(#{imagesdir}/))
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                if (front_cover_image = doc.attr 'front-cover-image')
         | 
| 64 | 
            +
                  if front_cover_image =~ InlineImageMacroRx
         | 
| 65 | 
            +
                    front_cover_image = %(#{imagesdir}#{$1})
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
                  workdir = doc.attr 'docdir'
         | 
| 68 | 
            +
                else
         | 
| 69 | 
            +
                  front_cover_image = DefaultCoverImage
         | 
| 70 | 
            +
                  workdir = DATA_DIR
         | 
| 71 | 
            +
                end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                resources workdir: workdir do
         | 
| 74 | 
            +
                  cover_image %(#{imagesdir}jacket/cover#{::File.extname front_cover_image}) => front_cover_image
         | 
| 75 | 
            +
                end
         | 
| 76 | 
            +
              end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
              # NOTE must be called within the ordered block
         | 
| 79 | 
            +
              def add_cover_page doc, spine_builder, book
         | 
| 80 | 
            +
                imagesdir = (doc.attr 'imagesdir', '.').chomp '/'
         | 
| 81 | 
            +
                imagesdir = (imagesdir == '.' ? nil : %(#{imagesdir}/))
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                img = (doc.attr 'front-cover-image') || DefaultCoverImage
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                if img =~ InlineImageMacroRx
         | 
| 86 | 
            +
                  img = %(#{imagesdir}#{$1})
         | 
| 87 | 
            +
                  # TODO use proper attribute parser
         | 
| 88 | 
            +
                  _, w, h = $2.split ',', 3
         | 
| 89 | 
            +
                end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                w ||= 1050
         | 
| 92 | 
            +
                h ||= 1600
         | 
| 93 | 
            +
                img_path = %(#{imagesdir}jacket/cover#{::File.extname img})
         | 
| 94 | 
            +
                # NOTE SVG wrapper maintains aspect ratio and confines image to view box
         | 
| 95 | 
            +
                content = %(<!DOCTYPE html>
         | 
| 96 | 
            +
            <html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" xml:lang="en" lang="en">
         | 
| 97 | 
            +
            <head>
         | 
| 98 | 
            +
            <meta charset="UTF-8"/>
         | 
| 99 | 
            +
            <title>#{sanitized_doctitle doc, :element_cdata}</title>
         | 
| 100 | 
            +
            <style type="text/css">
         | 
| 101 | 
            +
            @page {
         | 
| 102 | 
            +
              margin: 0;
         | 
| 103 | 
            +
            }
         | 
| 104 | 
            +
            html {
         | 
| 105 | 
            +
              margin: 0 !important;
         | 
| 106 | 
            +
              padding: 0 !important;
         | 
| 107 | 
            +
            }
         | 
| 108 | 
            +
            body {
         | 
| 109 | 
            +
              margin: 0;
         | 
| 110 | 
            +
              padding: 0 !important;
         | 
| 111 | 
            +
              text-align: center;
         | 
| 112 | 
            +
            }
         | 
| 113 | 
            +
            body > svg {
         | 
| 114 | 
            +
              /* prevent bleed onto second page (removes descender space) */
         | 
| 115 | 
            +
              display: block;
         | 
| 116 | 
            +
            }
         | 
| 117 | 
            +
            </style>
         | 
| 118 | 
            +
            </head>
         | 
| 119 | 
            +
            <body epub:type="cover"><svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
         | 
| 120 | 
            +
              width="100%" height="100%" viewBox="0 0 #{w} #{h}" preserveAspectRatio="xMidYMid meet">
         | 
| 121 | 
            +
            <image width="#{w}" height="#{h}" xlink:href="#{img_path}"/>
         | 
| 122 | 
            +
            </svg></body>
         | 
| 123 | 
            +
            </html>).to_ios
         | 
| 124 | 
            +
                # GitDen expects a cover.xhtml, so add it to the spine
         | 
| 125 | 
            +
                spine_builder.file 'cover.xhtml' => content
         | 
| 126 | 
            +
                spine_builder.id 'cover'
         | 
| 127 | 
            +
                # clearly a deficiency of gepub that it does not match the id correctly
         | 
| 128 | 
            +
                book.spine.itemref_by_id['item_cover1'].idref = 'cover'
         | 
| 129 | 
            +
              end
         | 
| 130 | 
            +
             | 
| 131 | 
            +
              def add_images_from_front_matter
         | 
| 132 | 
            +
                if ::File.exist? 'front-matter.html'
         | 
| 133 | 
            +
                  ::File.read('front-matter.html').scan(/<img src="(.+?)"/) do
         | 
| 134 | 
            +
                    resources do
         | 
| 135 | 
            +
                      file $1
         | 
| 136 | 
            +
                    end
         | 
| 137 | 
            +
                  end
         | 
| 138 | 
            +
                end
         | 
| 139 | 
            +
              end
         | 
| 140 | 
            +
             | 
| 141 | 
            +
              def add_front_matter_page doc, spine_builder, builder, format
         | 
| 142 | 
            +
                if ::File.exist? 'front-matter.html'
         | 
| 143 | 
            +
                  spine_builder.file 'front-matter.html' => (builder.postprocess_xhtml_file 'front-matter.html', format)
         | 
| 144 | 
            +
                  (spine_builder.instance_variable_get :@last_defined_item).properties << 'svg'
         | 
| 145 | 
            +
                end
         | 
| 146 | 
            +
              end
         | 
| 147 | 
            +
             | 
| 148 | 
            +
              # FIXME don't add same image more than once
         | 
| 149 | 
            +
              # FIXME add inline images
         | 
| 150 | 
            +
              def add_content_images doc, images
         | 
| 151 | 
            +
                docimagesdir = (doc.attr 'imagesdir', '.').chomp '/'
         | 
| 152 | 
            +
                docimagesdir = (docimagesdir == '.' ? nil : %(#{docimagesdir}/))
         | 
| 153 | 
            +
             | 
| 154 | 
            +
                resources(workdir: (doc.attr 'docdir')) do
         | 
| 155 | 
            +
                  images.each do |image|
         | 
| 156 | 
            +
                    imagesdir = (image.document.attr 'imagesdir', '.').chomp '/'
         | 
| 157 | 
            +
                    imagesdir = (imagesdir == '.' ? nil : %(#{imagesdir}/))
         | 
| 158 | 
            +
                    image_path = %(#{imagesdir}#{image.attr 'target'})
         | 
| 159 | 
            +
                    if image_path.start_with? %(#{docimagesdir}jacket/cover.)
         | 
| 160 | 
            +
                      warn %(The image path #{image_path} is reserved for the cover artwork. Ignoring conflicting image from content.)
         | 
| 161 | 
            +
                    elsif ::File.readable? image_path
         | 
| 162 | 
            +
                      file image_path
         | 
| 163 | 
            +
                    else
         | 
| 164 | 
            +
                      warn %(Image not found or not readable: #{image_path})
         | 
| 165 | 
            +
                    end
         | 
| 166 | 
            +
                  end
         | 
| 167 | 
            +
                end
         | 
| 168 | 
            +
              end
         | 
| 169 | 
            +
             | 
| 170 | 
            +
              def add_profile_images doc, usernames
         | 
| 171 | 
            +
                spine = @spine
         | 
| 172 | 
            +
                imagesdir = (doc.attr 'imagesdir', '.').chomp '/'
         | 
| 173 | 
            +
                imagesdir = (imagesdir == '.' ? nil : %(#{imagesdir}/))
         | 
| 174 | 
            +
             | 
| 175 | 
            +
                resources workdir: DATA_DIR do
         | 
| 176 | 
            +
                  file %(#{imagesdir}avatars/default.jpg) => %(images/default-avatar.jpg)
         | 
| 177 | 
            +
                  file %(#{imagesdir}headshots/default.jpg) => %(images/default-headshot.jpg)
         | 
| 178 | 
            +
                end
         | 
| 179 | 
            +
             | 
| 180 | 
            +
                resources(workdir: (doc.attr 'docdir')) do
         | 
| 181 | 
            +
                  usernames.each do |username|
         | 
| 182 | 
            +
                    if ::File.readable?(avatar = %(#{imagesdir}avatars/#{username}.jpg))
         | 
| 183 | 
            +
                      file avatar
         | 
| 184 | 
            +
                    else
         | 
| 185 | 
            +
                      warn %(Avatar #{avatar} not found or readable. Falling back to default avatar for #{username}.)
         | 
| 186 | 
            +
                      ::Dir.chdir DATA_DIR do
         | 
| 187 | 
            +
                        file avatar => %(images/default-avatar.jpg)
         | 
| 188 | 
            +
                      end
         | 
| 189 | 
            +
                    end
         | 
| 190 | 
            +
             | 
| 191 | 
            +
                    if ::File.readable? (headshot = %(#{imagesdir}headshots/#{username}.jpg))
         | 
| 192 | 
            +
                      file headshot
         | 
| 193 | 
            +
                    else
         | 
| 194 | 
            +
                      warn %(Headshot #{headshot} not found or readable. Falling back to default headshot for #{username}.)
         | 
| 195 | 
            +
                      ::Dir.chdir DATA_DIR do
         | 
| 196 | 
            +
                        file headshot => %(images/default-headshot.jpg)
         | 
| 197 | 
            +
                      end
         | 
| 198 | 
            +
                    end
         | 
| 199 | 
            +
                  end
         | 
| 200 | 
            +
            =begin
         | 
| 201 | 
            +
                  spine.each do |item|
         | 
| 202 | 
            +
                    username = (item.attr 'username') || 'default'
         | 
| 203 | 
            +
                    avatar_target = %(#{imagesdir}avatars/#{username}.jpg)
         | 
| 204 | 
            +
                    if ::File.readable?(avatar = %(#{item.attr 'docname'}/avatar.jpg))
         | 
| 205 | 
            +
                      file avatar_target => avatar
         | 
| 206 | 
            +
                    else
         | 
| 207 | 
            +
                      warn %(Avatar #{avatar} not found or not readable. Falling back to default avatar for #{username}.)
         | 
| 208 | 
            +
                      ::Dir.chdir DATA_DIR do
         | 
| 209 | 
            +
                        file avatar_target => %(images/default-avatar.jpg)
         | 
| 210 | 
            +
                      end
         | 
| 211 | 
            +
                    end
         | 
| 212 | 
            +
                    if ::File.readable? (headshot = %(#{item.attr 'docname'}/headshot.jpg))
         | 
| 213 | 
            +
                      file headshot
         | 
| 214 | 
            +
                      # TODO default headshot?
         | 
| 215 | 
            +
                    end
         | 
| 216 | 
            +
                  end
         | 
| 217 | 
            +
            =end
         | 
| 218 | 
            +
                end
         | 
| 219 | 
            +
              end
         | 
| 220 | 
            +
             | 
| 221 | 
            +
              def add_content doc
         | 
| 222 | 
            +
                builder = self
         | 
| 223 | 
            +
                spine = @spine
         | 
| 224 | 
            +
                format = @format
         | 
| 225 | 
            +
                resources(workdir: (doc.attr 'docdir')) do
         | 
| 226 | 
            +
                  builder.add_images_from_front_matter
         | 
| 227 | 
            +
                  # QUESTION should we move navigation_document to the Packager class? seems to make sense
         | 
| 228 | 
            +
                  #nav 'nav.xhtml' => (builder.postprocess_xhtml doc.converter.navigation_document(doc, spine), format)
         | 
| 229 | 
            +
                  nav 'nav.xhtml' => (builder.postprocess_xhtml ::Asciidoctor::Converter::Factory.default.create('epub3-xhtml5').navigation_document(doc, spine), format)
         | 
| 230 | 
            +
                  ordered do
         | 
| 231 | 
            +
                    builder.add_cover_page doc, self, @book unless format == :kf8
         | 
| 232 | 
            +
                    builder.add_front_matter_page doc, self, builder, format
         | 
| 233 | 
            +
                    spine.each_with_index do |item, i|
         | 
| 234 | 
            +
                      content_path = %(#{item.id || (item.attr 'docname')}.xhtml)
         | 
| 235 | 
            +
                      file content_path => (builder.postprocess_xhtml item.convert, format)
         | 
| 236 | 
            +
                      # NOTE heading for ePub2 navigation file; toc.ncx requires headings to be plain text
         | 
| 237 | 
            +
                      heading builder.sanitized_doctitle(item)
         | 
| 238 | 
            +
                      @last_defined_item.properties << 'svg' if ((item.attr 'epub-properties') || []).include? 'svg'
         | 
| 239 | 
            +
                      #linear 'yes' if i == 0
         | 
| 240 | 
            +
                    end
         | 
| 241 | 
            +
                  end
         | 
| 242 | 
            +
                end
         | 
| 243 | 
            +
              end
         | 
| 244 | 
            +
             | 
| 245 | 
            +
              def collect_keywords doc, spine
         | 
| 246 | 
            +
                ([doc] + spine).map do |item|
         | 
| 247 | 
            +
                  if item.attr? 'keywords'
         | 
| 248 | 
            +
                    (item.attr 'keywords').split CsvDelimiterRx
         | 
| 249 | 
            +
                  else
         | 
| 250 | 
            +
                    []
         | 
| 251 | 
            +
                  end
         | 
| 252 | 
            +
                end.flatten.uniq
         | 
| 253 | 
            +
              end
         | 
| 254 | 
            +
             | 
| 255 | 
            +
              # Swap fonts in CSS based on the value of the document attribute 'scripts',
         | 
| 256 | 
            +
              # then return the list of fonts as well as the font CSS.
         | 
| 257 | 
            +
              def select_fonts filename, scripts = 'latin'
         | 
| 258 | 
            +
                font_css = ::File.read(filename)
         | 
| 259 | 
            +
                font_css = font_css.gsub(/(?<=-)latin(?=\.ttf\))/, scripts) unless scripts == 'latin'
         | 
| 260 | 
            +
                font_list = font_css.scan(/url\(\.\.\/(.+\.ttf)\);$/).flatten
         | 
| 261 | 
            +
                return [font_list, font_css.to_ios]
         | 
| 262 | 
            +
              end
         | 
| 263 | 
            +
             | 
| 264 | 
            +
              def postprocess_css_file filename, format
         | 
| 265 | 
            +
                return filename unless format == :kf8
         | 
| 266 | 
            +
                postprocess_css ::File.read(filename), format
         | 
| 267 | 
            +
              end
         | 
| 268 | 
            +
             | 
| 269 | 
            +
              def postprocess_css content, format
         | 
| 270 | 
            +
                return content.to_ios unless format == :kf8
         | 
| 271 | 
            +
                # TODO convert regular expressions to constants
         | 
| 272 | 
            +
                content
         | 
| 273 | 
            +
                    .gsub(/^  -webkit-column-break-.*\n/, '')
         | 
| 274 | 
            +
                    .gsub(/^  max-width: .*\n/, '')
         | 
| 275 | 
            +
                    .to_ios
         | 
| 276 | 
            +
              end
         | 
| 277 | 
            +
             | 
| 278 | 
            +
              def postprocess_xhtml_file filename, format
         | 
| 279 | 
            +
                return filename unless format == :kf8
         | 
| 280 | 
            +
                postprocess_xhtml ::File.read(filename), format
         | 
| 281 | 
            +
              end
         | 
| 282 | 
            +
             | 
| 283 | 
            +
              # NOTE Kindle requires that
         | 
| 284 | 
            +
              #      <meta charset="utf-8"/>
         | 
| 285 | 
            +
              #      be converted to
         | 
| 286 | 
            +
              #      <meta http-equiv="Content-Type" content="application/xml+xhtml; charset=UTF-8"/>
         | 
| 287 | 
            +
              def postprocess_xhtml content, format
         | 
| 288 | 
            +
                return content.to_ios unless format == :kf8
         | 
| 289 | 
            +
                # TODO convert regular expressions to constants
         | 
| 290 | 
            +
                content
         | 
| 291 | 
            +
                    .gsub(/<meta charset="(.+?)"\/>/, '<meta http-equiv="Content-Type" content="application/xml+xhtml; charset=\1"/>')
         | 
| 292 | 
            +
                    .gsub(/<img([^>]+) style="width: (\d\d)%;"/, '<img\1 style="width: \2%; height: \2%;"')
         | 
| 293 | 
            +
                    .gsub(/<script type="text\/javascript">.*?<\/script>\n?/m, '')
         | 
| 294 | 
            +
                    .to_ios
         | 
| 295 | 
            +
              end
         | 
| 296 | 
            +
            end
         | 
| 297 | 
            +
             | 
| 298 | 
            +
            class Packager
         | 
| 299 | 
            +
              KINDLEGEN = ENV['KINDLEGEN'] || 'kindlegen'
         | 
| 300 | 
            +
              EPUBCHECK = ENV['EPUBCHECK'] || %(epubcheck#{::Gem.win_platform? ? '.bat' : '.sh'})
         | 
| 301 | 
            +
              EpubExtensionRx = /\.epub$/
         | 
| 302 | 
            +
              Kf8ExtensionRx = /-kf8\.epub$/
         | 
| 303 | 
            +
             | 
| 304 | 
            +
              def initialize spine_doc, spine, format = :epub3, options = {}
         | 
| 305 | 
            +
                @document = spine_doc
         | 
| 306 | 
            +
                @spine = spine || []
         | 
| 307 | 
            +
                @format = format
         | 
| 308 | 
            +
              end
         | 
| 309 | 
            +
             | 
| 310 | 
            +
              def package options = {}
         | 
| 311 | 
            +
                doc = @document
         | 
| 312 | 
            +
                spine = @spine
         | 
| 313 | 
            +
                fmt = @format
         | 
| 314 | 
            +
                dest = @dest_dir = options[:to_dir] || (File.expand_path ::Dir.pwd)
         | 
| 315 | 
            +
             | 
| 316 | 
            +
                images = spine.map {|item| (item.find_by context: :image) || [] }.flatten
         | 
| 317 | 
            +
                usernames = spine.map {|item| item.attr 'username' }.compact.uniq
         | 
| 318 | 
            +
                # FIXME authors should be aggregated already on parent document
         | 
| 319 | 
            +
                authors = if doc.attr? 'authors'
         | 
| 320 | 
            +
                  (doc.attr 'authors').split(GepubBuilderMixin::CsvDelimiterRx).concat(spine.map {|item| item.attr 'author' }).uniq
         | 
| 321 | 
            +
                else
         | 
| 322 | 
            +
                  []
         | 
| 323 | 
            +
                end
         | 
| 324 | 
            +
             | 
| 325 | 
            +
                builder = ::GEPUB::Builder.new do
         | 
| 326 | 
            +
                  extend GepubBuilderMixin
         | 
| 327 | 
            +
                  @document = doc
         | 
| 328 | 
            +
                  @spine = spine
         | 
| 329 | 
            +
                  @format = fmt
         | 
| 330 | 
            +
                  @book.epub_backward_compat = (fmt != :kf8)
         | 
| 331 | 
            +
             | 
| 332 | 
            +
                  language(doc.attr 'lang', 'en')
         | 
| 333 | 
            +
                  id 'pub-language'
         | 
| 334 | 
            +
             | 
| 335 | 
            +
                  if doc.attr? 'uuid'
         | 
| 336 | 
            +
                    unique_identifier doc.attr('uuid'), 'pub-identifier', 'uuid'
         | 
| 337 | 
            +
                  else
         | 
| 338 | 
            +
                    unique_identifier doc.id, 'pub-identifier', 'uuid'
         | 
| 339 | 
            +
                  end
         | 
| 340 | 
            +
                  # replace with next line once the attributes argument is supported
         | 
| 341 | 
            +
                  #unique_identifier doc.id, 'pub-id', 'uuid', 'scheme' => 'xsd:string'
         | 
| 342 | 
            +
             | 
| 343 | 
            +
                  title sanitized_doctitle(doc)
         | 
| 344 | 
            +
                  id 'pub-title'
         | 
| 345 | 
            +
             | 
| 346 | 
            +
                  # FIXME this logic needs some work
         | 
| 347 | 
            +
                  if doc.attr? 'publisher'
         | 
| 348 | 
            +
                    publisher(publisher_name = doc.attr('publisher'))
         | 
| 349 | 
            +
                    # marc role: Book producer (see http://www.loc.gov/marc/relators/relaterm.html)
         | 
| 350 | 
            +
                    creator doc.attr('producer', publisher_name), 'bkp'
         | 
| 351 | 
            +
                  else
         | 
| 352 | 
            +
                    # NOTE Use producer as both publisher and producer if publisher isn't specified
         | 
| 353 | 
            +
                    if doc.attr? 'producer'
         | 
| 354 | 
            +
                      producer_name = doc.attr 'producer'
         | 
| 355 | 
            +
                      publisher producer_name
         | 
| 356 | 
            +
                      # marc role: Book producer (see http://www.loc.gov/marc/relators/relaterm.html)
         | 
| 357 | 
            +
                      creator producer_name, 'bkp'
         | 
| 358 | 
            +
                    # NOTE Use author as creator if both publisher or producer are absent
         | 
| 359 | 
            +
                    elsif doc.attr? 'author'
         | 
| 360 | 
            +
                      # marc role: Author (see http://www.loc.gov/marc/relators/relaterm.html)
         | 
| 361 | 
            +
                      creator doc.attr('author'), 'aut'
         | 
| 362 | 
            +
                    end
         | 
| 363 | 
            +
                  end
         | 
| 364 | 
            +
             | 
| 365 | 
            +
                  if doc.attr? 'creator'
         | 
| 366 | 
            +
                    # marc role: Creator (see http://www.loc.gov/marc/relators/relaterm.html)
         | 
| 367 | 
            +
                    creator doc.attr('creator'), 'cre'
         | 
| 368 | 
            +
                  else
         | 
| 369 | 
            +
                    # marc role: Manufacturer (see http://www.loc.gov/marc/relators/relaterm.html)
         | 
| 370 | 
            +
                    # QUESTION should this be bkp?
         | 
| 371 | 
            +
                    creator 'Asciidoctor', 'mfr'
         | 
| 372 | 
            +
                  end
         | 
| 373 | 
            +
             | 
| 374 | 
            +
                  # TODO getting author list should be a method on Asciidoctor API
         | 
| 375 | 
            +
                  contributors(*authors)
         | 
| 376 | 
            +
             | 
| 377 | 
            +
                  if doc.attr? 'revdate'
         | 
| 378 | 
            +
                    # TODO ensure this is a real date
         | 
| 379 | 
            +
                    date(doc.attr 'revdate')
         | 
| 380 | 
            +
                  else
         | 
| 381 | 
            +
                    date ::Time.now.strftime('%Y-%m-%dT%H:%M:%SZ')
         | 
| 382 | 
            +
                  end
         | 
| 383 | 
            +
             | 
| 384 | 
            +
                  if doc.attr? 'description'
         | 
| 385 | 
            +
                    description(doc.attr 'description')
         | 
| 386 | 
            +
                  end
         | 
| 387 | 
            +
             | 
| 388 | 
            +
                  (collect_keywords doc, spine).each do |s|
         | 
| 389 | 
            +
                    subject s
         | 
| 390 | 
            +
                  end
         | 
| 391 | 
            +
             | 
| 392 | 
            +
                  if doc.attr? 'source'
         | 
| 393 | 
            +
                    source(doc.attr 'source')
         | 
| 394 | 
            +
                  end
         | 
| 395 | 
            +
             | 
| 396 | 
            +
                  if doc.attr? 'copyright'
         | 
| 397 | 
            +
                    rights(doc.attr 'copyright')
         | 
| 398 | 
            +
                  end
         | 
| 399 | 
            +
             | 
| 400 | 
            +
                  #add_metadata 'ibooks:specified-fonts', true 
         | 
| 401 | 
            +
             | 
| 402 | 
            +
                  add_theme_assets doc
         | 
| 403 | 
            +
                  add_cover_image doc
         | 
| 404 | 
            +
                  add_profile_images doc, usernames
         | 
| 405 | 
            +
                  # QUESTION move add_content_images to add_content method?
         | 
| 406 | 
            +
                  add_content_images doc, images
         | 
| 407 | 
            +
                  add_content doc
         | 
| 408 | 
            +
                end
         | 
| 409 | 
            +
             | 
| 410 | 
            +
                ::FileUtils.mkdir_p dest unless ::File.directory? dest
         | 
| 411 | 
            +
             | 
| 412 | 
            +
                epub_file = ::File.expand_path %(#{doc.attr 'docname'}#{fmt == :kf8 ? '-kf8' : nil}.epub), dest
         | 
| 413 | 
            +
                builder.generate_epub epub_file
         | 
| 414 | 
            +
                puts %(Wrote #{fmt.upcase} to #{epub_file})
         | 
| 415 | 
            +
                if options[:extract]
         | 
| 416 | 
            +
                  extract_dir = epub_file.sub EpubExtensionRx, ''
         | 
| 417 | 
            +
                  ::FileUtils.remove_dir extract_dir if ::File.directory? extract_dir
         | 
| 418 | 
            +
                  ::Dir.mkdir extract_dir
         | 
| 419 | 
            +
                  ::Dir.chdir extract_dir do
         | 
| 420 | 
            +
                    ::Zip::File.open epub_file do |entries|
         | 
| 421 | 
            +
                      entries.each do |entry|
         | 
| 422 | 
            +
                        next unless entry.file?
         | 
| 423 | 
            +
                        unless (entry_dir = ::File.dirname entry.name) == '.' || (::File.directory? entry_dir)
         | 
| 424 | 
            +
                          ::FileUtils.mkdir_p entry_dir
         | 
| 425 | 
            +
                        end
         | 
| 426 | 
            +
                        entry.extract
         | 
| 427 | 
            +
                      end
         | 
| 428 | 
            +
                    end
         | 
| 429 | 
            +
                  end
         | 
| 430 | 
            +
                  puts %(Extracted #{fmt.upcase} to #{extract_dir})
         | 
| 431 | 
            +
                end
         | 
| 432 | 
            +
             | 
| 433 | 
            +
                if fmt == :kf8
         | 
| 434 | 
            +
                  distill_epub_to_mobi epub_file
         | 
| 435 | 
            +
                elsif options[:validate]
         | 
| 436 | 
            +
                  validate_epub epub_file
         | 
| 437 | 
            +
                end
         | 
| 438 | 
            +
              end
         | 
| 439 | 
            +
             | 
| 440 | 
            +
              # QUESTION how to enable the -c2 flag? (enables ~3-5% compression)
         | 
| 441 | 
            +
              def distill_epub_to_mobi epub_file
         | 
| 442 | 
            +
                kindlegen_cmd = KINDLEGEN
         | 
| 443 | 
            +
                unless ::File.executable? kindlegen_cmd
         | 
| 444 | 
            +
                  require 'kindlegen' unless defined? ::Kindlegen
         | 
| 445 | 
            +
                  kindlegen_cmd = ::Kindlegen.command
         | 
| 446 | 
            +
                end
         | 
| 447 | 
            +
                mobi_file = ::File.basename(epub_file).sub Kf8ExtensionRx, '.mobi'
         | 
| 448 | 
            +
                ::Open3.popen2e(::Shellwords.join [kindlegen_cmd, '-o', mobi_file, epub_file]) {|input, output, wait_thr|
         | 
| 449 | 
            +
                  output.each {|line| puts line }
         | 
| 450 | 
            +
                }
         | 
| 451 | 
            +
                puts %(Wrote MOBI to #{::File.join ::File.dirname(epub_file), mobi_file})
         | 
| 452 | 
            +
              end
         | 
| 453 | 
            +
             | 
| 454 | 
            +
              def validate_epub epub_file
         | 
| 455 | 
            +
                epubcheck_cmd = EPUBCHECK
         | 
| 456 | 
            +
                unless ::File.executable? epubcheck_cmd
         | 
| 457 | 
            +
                  epubcheck_cmd = ::Gem.bin_path 'epubcheck', 'epubcheck' 
         | 
| 458 | 
            +
                end
         | 
| 459 | 
            +
                # NOTE epubcheck gem doesn't support epubcheck command options; enable -quiet once supported
         | 
| 460 | 
            +
                ::Open3.popen2e(::Shellwords.join [epubcheck_cmd, epub_file]) {|input, output, wait_thr|
         | 
| 461 | 
            +
                  output.each {|line| puts line }
         | 
| 462 | 
            +
                }
         | 
| 463 | 
            +
              end
         | 
| 464 | 
            +
            end
         | 
| 465 | 
            +
            end
         | 
| 466 | 
            +
            end
         |