1 | /*
|
2 | * Copyright (C) 2017 Alasdair Mercer, !ninja
|
3 | *
|
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
|
5 | * of this software and associated documentation files (the "Software"), to deal
|
6 | * in the Software without restriction, including without limitation the rights
|
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8 | * copies of the Software, and to permit persons to whom the Software is
|
9 | * furnished to do so, subject to the following conditions:
|
10 | *
|
11 | * The above copyright notice and this permission notice shall be included in all
|
12 | * copies or substantial portions of the Software.
|
13 | *
|
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
20 | * SOFTWARE.
|
21 | */
|
22 |
|
23 | ;
|
24 |
|
25 | const omit = require('lodash.omit');
|
26 | const pick = require('lodash.pick');
|
27 |
|
28 | const Converter = require('./Converter');
|
29 |
|
30 | const _provider = Symbol('provider');
|
31 |
|
32 | /**
|
33 | * The application programming interface for a SVG converter {@link Provider}.
|
34 | *
|
35 | * @public
|
36 | */
|
37 | class API {
|
38 |
|
39 | /**
|
40 | * Creates an instance of {@link API} using the specified <code>provider</code>.
|
41 | *
|
42 | * @param {Provider} provider - the {@link Provider} to be used
|
43 | * @public
|
44 | */
|
45 | constructor(provider) {
|
46 | this[_provider] = provider;
|
47 |
|
48 | // Workaround for #22 by ensuring all public methods are bound to this instance
|
49 | this.convert = this.convert.bind(this);
|
50 | this.convertFile = this.convertFile.bind(this);
|
51 | this.createConverter = this.createConverter.bind(this);
|
52 | }
|
53 |
|
54 | /**
|
55 | * Converts the specified <code>input</code> SVG into another format using the <code>options</code> provided via a
|
56 | * headless Chromium instance.
|
57 | *
|
58 | * <code>input</code> can either be a SVG buffer or string.
|
59 | *
|
60 | * If the width and/or height cannot be derived from <code>input</code> then they must be provided via their
|
61 | * corresponding options. This method attempts to derive the dimensions from <code>input</code> via any
|
62 | * <code>width</code>/<code>height</code> attributes or its calculated <code>viewBox</code> attribute.
|
63 | *
|
64 | * This method is resolved with the converted output buffer.
|
65 | *
|
66 | * An error will occur if both the <code>baseFile</code> and <code>baseUrl</code> options have been provided,
|
67 | * <code>input</code> does not contain an SVG element or no <code>width</code> and/or <code>height</code> options were
|
68 | * provided and this information could not be derived from <code>input</code>.
|
69 | *
|
70 | * @param {Buffer|string} input - the SVG input to be converted to another format
|
71 | * @param {API~ConvertOptions} [options] - the options to be used
|
72 | * @return {Promise.<Buffer, Error>} A <code>Promise</code> that is resolved with the converted output buffer.
|
73 | * @public
|
74 | */
|
75 | async convert(input, options) {
|
76 | const converter = this.createConverter(pick(options, 'puppeteer'));
|
77 | let output;
|
78 |
|
79 | try {
|
80 | output = await converter.convert(input, omit(options, 'puppeteer'));
|
81 | } finally {
|
82 | await converter.destroy();
|
83 | }
|
84 |
|
85 | return output;
|
86 | }
|
87 |
|
88 | /**
|
89 | * Converts the SVG file at the specified path into another format using the <code>options</code> provided and writes
|
90 | * it to the output file.
|
91 | *
|
92 | * The output file is derived from <code>inputFilePath</code> unless the <code>outputFilePath</code> option is
|
93 | * specified.
|
94 | *
|
95 | * If the width and/or height cannot be derived from the input file then they must be provided via their corresponding
|
96 | * options. This method attempts to derive the dimensions from the input file via any
|
97 | * <code>width</code>/<code>height</code> attributes or its calculated <code>viewBox</code> attribute.
|
98 | *
|
99 | * This method is resolved with the path of the converted output file for reference.
|
100 | *
|
101 | * An error will occur if both the <code>baseFile</code> and <code>baseUrl</code> options have been provided, the
|
102 | * input file does not contain an SVG element, no <code>width</code> and/or <code>height</code> options were provided
|
103 | * and this information could not be derived from input file, or a problem arises while reading the input file or
|
104 | * writing the output file.
|
105 | *
|
106 | * @param {string} inputFilePath - the path of the SVG file to be converted to another file format
|
107 | * @param {API~ConvertFileOptions} [options] - the options to be used
|
108 | * @return {Promise.<string, Error>} A <code>Promise</code> that is resolved with the output file path.
|
109 | * @public
|
110 | */
|
111 | async convertFile(inputFilePath, options) {
|
112 | const converter = this.createConverter(pick(options, 'puppeteer'));
|
113 | let outputFilePath;
|
114 |
|
115 | try {
|
116 | outputFilePath = await converter.convertFile(inputFilePath, omit(options, 'puppeteer'));
|
117 | } finally {
|
118 | await converter.destroy();
|
119 | }
|
120 |
|
121 | return outputFilePath;
|
122 | }
|
123 |
|
124 | /**
|
125 | * Creates an instance of {@link Converter} using the <code>options</code> provided.
|
126 | *
|
127 | * It is important to note that, after the first time either {@link Converter#convert} or
|
128 | * {@link Converter#convertFile} are called, a headless Chromium instance will remain open until
|
129 | * {@link Converter#destroy} is called. This is done automatically when using the {@link API} convert methods,
|
130 | * however, when using {@link Converter} directly, it is the responsibility of the caller. Due to the fact that
|
131 | * creating browser instances is expensive, this level of control allows callers to reuse a browser for multiple
|
132 | * conversions. For example; one could create a {@link Converter} and use it to convert a collection of SVG files to
|
133 | * files in another format and then destroy it afterwards. It's not recommended to keep an instance around for too
|
134 | * long, as it will use up resources.
|
135 | *
|
136 | * @param {API~CreateConverterOptions} [options] - the options to be used
|
137 | * @return {Converter} A newly created {@link Converter} instance.
|
138 | * @public
|
139 | */
|
140 | createConverter(options) {
|
141 | return new Converter(this.provider, options);
|
142 | }
|
143 |
|
144 | /**
|
145 | * Returns the {@link Provider} for this {@link Converter}.
|
146 | *
|
147 | * @return {Provider} The provider.
|
148 | * @public
|
149 | */
|
150 | get provider() {
|
151 | return this[_provider];
|
152 | }
|
153 |
|
154 | /**
|
155 | * Returns the current version of the SVG converter provider.
|
156 | *
|
157 | * @return {string} The version.
|
158 | * @public
|
159 | */
|
160 | get version() {
|
161 | return this.provider.getVersion();
|
162 | }
|
163 |
|
164 | }
|
165 |
|
166 | module.exports = API;
|
167 |
|
168 | /**
|
169 | * The options that can be passed to {@link API#convertFile}.
|
170 | *
|
171 | * @typedef {Converter~ConvertFileOptions} API~ConvertFileOptions
|
172 | * @property {Object} [puppeteer] - The options that are to be passed directly to <code>puppeteer.launch</code> when
|
173 | * creating the <code>Browser</code> instance.
|
174 | */
|
175 |
|
176 | /**
|
177 | * The options that can be passed to {@link API#convert}.
|
178 | *
|
179 | * @typedef {Converter~ConvertOptions} API~ConvertOptions
|
180 | * @property {Object} [puppeteer] - The options that are to be passed directly to <code>puppeteer.launch</code> when
|
181 | * creating the <code>Browser</code> instance.
|
182 | */
|
183 |
|
184 | /**
|
185 | * The options that can be passed to {@link API#createConverter}.
|
186 | *
|
187 | * @typedef {Converter~Options} API~CreateConverterOptions
|
188 | * @property {Object} [puppeteer] - The options that are to be passed directly to <code>puppeteer.launch</code> when
|
189 | * creating the <code>Browser</code> instance.
|
190 | */
|