├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── FieldtypePDF.module.php
├── FieldtypePDF
├── PDFConverter.php
├── PagePDF.php
└── PagePDFs.php
├── InputfieldPDF.css
├── InputfieldPDF.module.php
├── LICENSE
├── README.md
├── apigen.neon
├── composer.json
├── phpunit.xml
├── tense.yml
└── test
├── FieldtypePDFTest.php
├── asset
├── test-custom.jpg
├── test.jpg
└── test.pdf
└── bootstrap.php
/.gitignore:
--------------------------------------------------------------------------------
1 | .tense
2 | .ampp
3 | /nbproject
4 | /vendor
5 | /doc
6 | /test/log
7 | /test/config.sh
8 | composer.lock
9 | tense.local.yml
10 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 5.6
5 |
6 | env:
7 | global:
8 | secure: LcmdRBX5bX8BbpHewAlUwBCRAzCkUvS+XdvY5Bjlbg2NdbAsQ1nPJfGVVfP2wb/vKOeU++6eZASi6X0MWlm1mdCp/6/nu5jSnFrkP/rokddpsictSL2GyM/C9bp45MZpshy6DqY8zoEfKtu9ZvHtVccdErVRrVlOr3ebcfB2uiw=
9 |
10 | before_script:
11 | - git config --global user.email "travis@travis-ci.org"
12 | - git config --global user.name "Travis"
13 |
14 | script:
15 | - echo "TODO testing"
16 |
17 | after_success:
18 | - wget https://gist.githubusercontent.com/uiii/1fc5373c6f58ba29fb33/raw/generate-api.sh
19 | - export REPO_SLUG=${TRAVIS_REPO_SLUG}
20 | - export API_VERSION=${TRAVIS_BRANCH}
21 | - if [ ${TRAVIS_PULL_REQUEST} = 'false' ]; then sh generate-api.sh; fi
22 |
23 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 2.0.0 (2020-30-12)
2 |
3 | ### Changed
4 | - Refactor for PW 3.x
5 |
6 | ### Deprecated
7 | - Drop PW 2.x support
8 |
9 | ## 1.2.0 (2020-30-12)
10 |
11 | Use version 1.x for backward compatibility with ProcessWire 2.x
12 |
13 | ### Changes
14 | - Remove PW 3.x related things
15 |
16 | ## 1.1.5 (2018-04-05)
17 |
18 | ### Fixed
19 | - Making PW 3.x namespace fixing reliable.
20 |
21 | ## 1.1.4 (2017-08-08)
22 |
23 | ### Fixed
24 | - Fixed module upgrading on PW 3.x [[issue #12](https://github.com/uiii/ProcessWire-FieldtypePDF/issues/12)]
25 |
26 | ## 1.1.3 (2017-04-19)
27 |
28 | ### Changed
29 | - Use [Tense](https://github.com/uiii/tense) for testing against multiple versions of ProcessWire
30 |
31 | ## 1.1.2 (2016-12-09)
32 |
33 | ### Added
34 | - ProcessWire 3.x support
35 | - Module is installable via Composer
36 |
37 | ### Changed
38 | - Use [PW-Test](https://github.com/uiii/pw-test) for testing against multiple versions of ProcessWire
39 |
40 | ## 1.1.1 (2016-08-26)
41 |
42 | ### Fixed
43 | - Fixed module's installation by classname
44 |
45 | ## 1.1.0 (2016-08-26)
46 |
47 | ### Added
48 | - PDF to image converter is now configurable in admin [[issue #7](https://github.com/uiii/ProcessWire-FieldtypePDF/issues/7)]
49 | - You can specify PDF's page number to generate thumbnail [[issue #3](https://github.com/uiii/ProcessWire-FieldtypePDF/issues/3)]
50 | - Fix bugs [[issue #4](https://github.com/uiii/ProcessWire-FieldtypePDF/issues/4), [issue #6](https://github.com/uiii/ProcessWire-FieldtypePDF/issues/6)]
51 | - Add ApiGen config for API documentation generation
52 | - Add PHPUnit tests
53 | - Add license (MIT)
54 |
55 | ### Deprecated
56 | - Deprecated `thumbnail` method, use `toImage` instead.
57 | - Deprecated `isThumbnail` method, use `isImageOfThis` instead.
58 | - Deprecated `removeThumbnails` method, use `removeImages` instead.
59 |
60 | ## 1.0.1 (2014-05-29)
61 |
62 | ### Added
63 | - Added module requirements check [[issue #2](https://github.com/uiii/ProcessWire-FieldtypePDF/issues/2)]
64 | - Set important ImageMagick settings before conversion [[issue #1](https://github.com/uiii/ProcessWire-FieldtypePDF/issues/1)]
65 |
--------------------------------------------------------------------------------
/FieldtypePDF.module.php:
--------------------------------------------------------------------------------
1 | (http://uiii.cz)
7 | *
8 | * Permission is hereby granted, free of charge, to any person obtaining a copy
9 | * of this software and associated documentation files (the "Software"), to deal
10 | * in the Software without restriction, including without limitation the rights
11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | * copies of the Software, and to permit persons to whom the Software is
13 | * furnished to do so, subject to the following conditions:
14 | *
15 | * The above copyright notice and this permission notice shall be included in
16 | * all copies or substantial portions of the Software.
17 | *
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | * THE SOFTWARE.
25 | */
26 |
27 | namespace ProcessWire;
28 |
29 | use FieldtypePDF\PagePDF;
30 | use FieldtypePDF\PagePDFs;
31 | use FieldtypePDF\PDFConverter;
32 |
33 | class FieldtypePDF extends FieldtypeFile implements ConfigurableModule
34 | {
35 | protected static $defaults = array(
36 | 'fallbackMode' => false
37 | );
38 |
39 | public static function getModuleInfo()
40 | {
41 | return array(
42 | 'version' => 201,
43 | 'title' => __('PDF with thumbnail', __FILE__),
44 | 'summary' => __('Field that stores one or more PDF files allowing thumbnail creation.', __FILE__),
45 | 'href' => 'http://modules.processwire.com/modules/fieldtype-pdf',
46 | 'author' => 'Richard Jedlička',
47 | 'installs' => 'InputfieldPDF',
48 | 'autoload' => true,
49 | 'requires' => array(
50 | 'ProcessWire>=3.0.0'
51 | )
52 | );
53 | }
54 |
55 | public function init()
56 | {
57 | spl_autoload_register(function($classname) {
58 | $classname = ltrim($classname, '\\');
59 | $filename = sprintf('%s/%s.php', __DIR__, str_replace('\\', DIRECTORY_SEPARATOR, $classname));
60 |
61 | if (is_file($filename)) {
62 | require_once $filename;
63 | }
64 | });
65 | }
66 |
67 | public function ___install()
68 | {
69 | if(! class_exists('Imagick')) {
70 | throw new WireException(__('FieldtypePDF module requires the ImageMagick PHP extension.'));
71 | }
72 | }
73 |
74 | public function set($key, $value)
75 | {
76 | if($key === 'converterImagickOptions' && is_string($value)) {
77 | $value = explode("\n", $value);
78 | }
79 |
80 | return parent::set($key, $value);
81 | }
82 |
83 | public function getBlankValue(Page $page, Field $field)
84 | {
85 | $pagePDFs = new PagePDFs($page);
86 | $pagePDFs->setField($field);
87 | $pagePDFs->setTrackChanges(true);
88 |
89 | return $pagePDFs;
90 | }
91 |
92 | protected function getBlankPagefile(Pagefiles $pagefiles, $filename)
93 | {
94 | return new PagePDF($pagefiles, $filename);
95 | }
96 |
97 | protected function getDefaultFileExtensions()
98 | {
99 | return 'pdf';
100 | }
101 |
102 | public function ___getConfigInputfields(Field $field)
103 | {
104 | $inputfields = parent::___getConfigInputfields($field);
105 |
106 | // hide input extensions field
107 | $extensionsInputField = $inputfields->get('extensions');
108 | $extensionsInputField->collapsed = Inputfield::collapsedHidden;
109 |
110 | // add fields for thumbnail creation settings
111 | $converterOptions = PDFConverter::$defaultOptions;
112 |
113 | /** @var InputfieldFieldset */
114 | $thumbnailFieldset = $this->modules->get('InputfieldFieldset');
115 | $thumbnailFieldset->label = $this->_('PDF to image converter');
116 | $thumbnailFieldset->description = $this->_('Options used when creating images from PDF files.');
117 |
118 | /** @var InputfieldText */
119 | $formatField = $this->modules->get('InputfieldText');
120 | $formatField->attr('name', 'converterFormat');
121 | $formatField->attr('value', $field->converterFormat ?: $converterOptions['format']);
122 | $formatField->label = $this->_('Image format');
123 | $formatField->description = $this->_('Format used when creating the image (recomeneded are JPEG or PNG). Don\'t forget to check the file extension.');
124 | $url = 'http://www.imagemagick.org/script/formats.php#supported';
125 | $formatField->notes = $this->_("For supported formats see [$url]($url)");
126 | $formatField->required = true;
127 | $thumbnailFieldset->add($formatField);
128 |
129 | /** @var InputfieldText */
130 | $extensionField = $this->modules->get('InputfieldText');
131 | $extensionField->attr('name', 'imageExtenstion');
132 | $extensionField->attr('value', $field->imageExtension ?: PagePDF::$defaultImageExtension);
133 | $extensionField->label = $this->_('File extension');
134 | $extensionField->description = $this->_('Sould correspond the image format.');
135 | $extensionField->required = true;
136 | $thumbnailFieldset->add($extensionField);
137 |
138 | if (! $this->fallbackMode) {
139 | /** @var InputfieldText */
140 | $backgroundField = $this->modules->get('InputfieldText');
141 | $backgroundField->attr('name', 'converterBackground');
142 | $backgroundField->attr('value', $field->converterBackground ?: $converterOptions['background']);
143 | $backgroundField->label = $this->_('Image background');
144 | $backgroundField->description = $this->_('Color used as a background for transparent PDFs. Enter \'transparent\' if you don\'t want the background to be set. Default color is white.');
145 | $url = 'http://www.imagemagick.org/script/color.php';
146 | $backgroundField->notes = $this->_("For supported colors see [$url]($url)");
147 | $thumbnailFieldset->add($backgroundField);
148 |
149 | /** @var InputfieldFieldset */
150 | $imagickFieldset = $this->modules->get('InputfieldFieldset');
151 | $imagickFieldset->label = $this->_('ImageMagick settings (advanced)');
152 | $imagickFieldset->collapsed = Inputfield::collapsedYes;
153 | $imagickFieldset->description = $this->_('Settings set to the ImageMagick instance before reading the PDF file. **Change this only if you know what you are doing.**');
154 |
155 | /** @var InputfieldText */
156 | $resolutionField = $this->modules->get('InputfieldText');
157 | $resolutionField->attr('name', 'converterResolution');
158 | $resolutionField->attr('value', $field->converterResolution ?: $converterOptions['resolution']);
159 | $resolutionField->label = $this->_('density');
160 | $url = 'http://www.imagemagick.org/script/command-line-options.php#density';
161 | $resolutionField->notes = $this->_("see [$url]($url)");
162 |
163 | /** @var InputfieldSelect */
164 | $colorspaceField = $this->modules->get('InputfieldSelect');
165 | $colorspaceField->attr('name', 'converterColorspace');
166 | $colorspaceField->label = $this->_('Color space');
167 | $colorspaceField->description = $this->_('Leave empty if you don\'t want to set.');
168 |
169 | $imagickReflection = new \ReflectionClass('Imagick');
170 | foreach ($imagickReflection->getConstants() as $name => $value) {
171 | if (preg_match('/^COLORSPACE_([^_]+)$/', $name, $matches)) {
172 | $name = $matches[1];
173 |
174 | if ($name === 'UNDEFINED') {
175 | $colorspaceField->addOption($value, '');
176 | } else {
177 | $colorspaceField->addOption($value, $name);
178 | }
179 | }
180 | }
181 |
182 | $colorspaceField->attr('value', $field->converterColorspace === null ? $converterOptions['colorspace'] : $field->converterColorspace);
183 |
184 | if (! PDFConverter::isColorspaceSupported()) {
185 | $colorspaceField->attr('disabled', true);
186 | $colorspaceField->addOption(\Imagick::COLORSPACE_UNDEFINED, 'not supported');
187 | $colorspaceField->notes = $this->_('Supported since ImageMagick version 6.5.7');
188 | }
189 |
190 | /** @var InputfieldTextArea */
191 | $optionsField = $this->modules->get('InputfieldTextarea');
192 | $optionsField->attr('name', 'converterImagickOptions');
193 | $optionsField->attr('rows', 3);
194 | $optionsField->label = $this->_('Options');
195 | $optionsField->description = $this->_('One definition per line (key=value).');
196 | $url = 'http://www.imagemagick.org/script/command-line-options.php#define';
197 | $optionsField->notes = $this->_("See [$url]($url)");
198 |
199 | $optionsField->attr('value', $field->converterImagickOptions ?: implode("\n", $converterOptions['imagickOptions']));
200 |
201 | $imagickFieldset->append($resolutionField);
202 | $imagickFieldset->append($colorspaceField);
203 | $imagickFieldset->append($optionsField);
204 |
205 | $thumbnailFieldset->add($imagickFieldset);
206 | } else {
207 | $thumbnailFieldset->notes = $this->_(
208 | '**Fallback mode is ON:** This will produce low quality images and some field type options won\'t be available, but may work where normal mode doesn\'t.' .
209 | 'You can turn it OFF [here](' . $this->config->urls->admin . 'module/edit?name=' . $this->name . ').'
210 | );
211 | }
212 |
213 | $inputfields->add($thumbnailFieldset);
214 |
215 | return $inputfields;
216 | }
217 |
218 | public static function getModuleConfigInputfields(array $data)
219 | {
220 | $data = array_merge(self::$defaults, $data);
221 |
222 | $modules = wire('modules');
223 |
224 | $inputfields = new InputfieldWrapper;
225 |
226 | $field = $modules->get('InputfieldCheckbox');
227 | $field->attr('name', 'fallbackMode');
228 | $field->attr('checked', $data['fallbackMode']);
229 | $field->label = __('Fallback mode');
230 | $field->description = __('Check this when you have troubles with generating image (e.g. GhostScript errors).');
231 | $field->notes = __('**Warning:** This will produce low quality images and some field type options won\'t be available, but may work where normal mode doesn\'t.');
232 |
233 | $inputfields->add($field);
234 |
235 | return $inputfields;
236 | }
237 | }
238 |
--------------------------------------------------------------------------------
/FieldtypePDF/PDFConverter.php:
--------------------------------------------------------------------------------
1 | (http://uiii.cz)
7 | *
8 | * Permission is hereby granted, free of charge, to any person obtaining a copy
9 | * of this software and associated documentation files (the "Software"), to deal
10 | * in the Software without restriction, including without limitation the rights
11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | * copies of the Software, and to permit persons to whom the Software is
13 | * furnished to do so, subject to the following conditions:
14 | *
15 | * The above copyright notice and this permission notice shall be included in
16 | * all copies or substantial portions of the Software.
17 | *
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | * THE SOFTWARE.
25 | */
26 |
27 | namespace FieldtypePDF;
28 |
29 | use Imagick;
30 |
31 | /**
32 | * PDF to image converter
33 | */
34 | class PDFConverter
35 | {
36 | /**
37 | * @var array
38 | */
39 | public static $defaultOptions = array(
40 | 'format' => 'JPEG',
41 | 'extension' => 'jpg',
42 | 'background' => '#FFFFFF',
43 | 'resolution' => '300x300',
44 | 'colorspace' => Imagick::COLORSPACE_RGB,
45 | 'imagickOptions' => array(
46 | 'pdf:use-cropbox=true'
47 | ),
48 | 'fallbackMode' => false
49 | );
50 |
51 | /**
52 | * @var array
53 | */
54 | protected $options;
55 |
56 | /**
57 | *
58 | * @var string
59 | */
60 | protected $pdfFilename;
61 |
62 | /**
63 | * Check if colorspace is supported by ImageMagick.
64 | *
65 | * @return bool
66 | */
67 | public static function isColorspaceSupported()
68 | {
69 | return method_exists('Imagick', 'setColorspace');
70 | }
71 |
72 | /**
73 | * Constructor
74 | *
75 | * @param string $pdfFilename PDF file to be converted
76 | * @param array $options Converter options (Optional)
77 | * @see setOptions()
78 | */
79 | public function __construct($pdfFilename, $options = array())
80 | {
81 | $this->pdfFilename = $pdfFilename;
82 | $this->setOptions($options);
83 | }
84 |
85 | /**
86 | * Set converter options
87 | *
88 | * @param array $options
89 | *
90 | * Available options are:
91 | *
92 | * - format (string) - format of the image
93 | * - extension (string) - image file extension
94 | * - background (string) - image background (used when the PDF's background is transparent),
95 | * to leave the background transparent set NULL
96 | * - resolution (string) - resolution used when reading the PDF (e.g. '300x300')
97 | * - colorspace (int) - colorspace used when reading the PDF (Imagick::COLORSPACE_* constant)
98 | * - imagickOptions (string[]) - ImageMagick options (each option in format 'key=value')
99 | * - fallbackMode (bool) - Fallback mode (produces low quality images, but may work where normal mode don't)
100 | *
101 | * For converter default options see {@link $defaultOptions}
102 | */
103 | public function setOptions(array $options)
104 | {
105 | $options = array_replace(self::$defaultOptions, $options);
106 |
107 | if (isset($options['resolution']) && $options['resolution']) {
108 | $resolution = $options['resolution'];
109 |
110 | if (is_string($resolution)) {
111 | $resolution = explode('x', $options['resolution']);
112 | } elseif (is_numeric($resolution)) {
113 | $resolution = array($resolution);
114 | }
115 |
116 | if (is_array($resolution)) {
117 | // append the resolution's second dimension if not set
118 | if (count($resolution) === 1) {
119 | $resolution[] = $resolution[0];
120 | }
121 |
122 | $options['resolution'] = $resolution;
123 | }
124 | }
125 |
126 | if (isset($options['imagickOptions']) && ! is_array($options['imagickOptions'])) {
127 | $options['imagickOptions'] = array($options['imagickOptions']);
128 | }
129 |
130 | $this->options = $options;
131 | }
132 |
133 | /**
134 | * Get converter options
135 | *
136 | * @return array
137 | */
138 | public function getOptions()
139 | {
140 | return $this->options;
141 | }
142 |
143 | /**
144 | * Convert PDF to image specified by filename.
145 | *
146 | * @param int $page Number of PDF's page to be converted to image (indexed from 0)
147 | * @param string $imageFilename Filename of output image
148 | */
149 | public function toImage($page, $imageFilename)
150 | {
151 | $options = $this->options;
152 |
153 | $imagick = new Imagick();
154 | $backgroundImagick = new Imagick();
155 |
156 | if ($options['fallbackMode']) {
157 | $imagick->clear();
158 | $imagick = new Imagick(sprintf('%s[%s]', $this->pdfFilename, $page));
159 | } else {
160 | if ($options['resolution']) {
161 | $resolution = $options['resolution'];
162 | $imagick->setResolution($resolution[0], $resolution[1]);
163 | $backgroundImagick->setResolution($resolution[0], $resolution[1]);
164 | }
165 |
166 | foreach($options['imagickOptions'] as $defition) {
167 | $defition = explode('=', $defition);
168 | $imagick->setOption($defition[0], $defition[1]);
169 | }
170 |
171 | if ($options['colorspace'] && self::isColorspaceSupported()) {
172 | $imagick->setColorspace($options['colorspace']);
173 | }
174 |
175 | $imagick->readimage(sprintf('%s[%s]', $this->pdfFilename, $page));
176 | }
177 |
178 | $image = $imagick;
179 |
180 | if ($options['background'] !== 'transparent') {
181 | $backgroundImagick->newImage(
182 | $imagick->getimagewidth(),
183 | $imagick->getimageheight(),
184 | $options['fallbackMode'] ? self::$defaultOptions['background'] : $options['background']
185 | );
186 |
187 | $backgroundImagick->compositeimage($imagick, Imagick::COMPOSITE_OVER, 0, 0);
188 | $image = $backgroundImagick;
189 | }
190 |
191 | $image->writeImage(sprintf('%s:%s', $options['format'], $imageFilename));
192 |
193 | $backgroundImagick->clear();
194 | $imagick->clear();
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/FieldtypePDF/PagePDF.php:
--------------------------------------------------------------------------------
1 | (http://uiii.cz)
7 | *
8 | * Permission is hereby granted, free of charge, to any person obtaining a copy
9 | * of this software and associated documentation files (the "Software"), to deal
10 | * in the Software without restriction, including without limitation the rights
11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | * copies of the Software, and to permit persons to whom the Software is
13 | * furnished to do so, subject to the following conditions:
14 | *
15 | * The above copyright notice and this permission notice shall be included in
16 | * all copies or substantial portions of the Software.
17 | *
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | * THE SOFTWARE.
25 | */
26 |
27 | namespace FieldtypePDF;
28 |
29 | use DirectoryIterator;
30 | use Exception;
31 |
32 | use ProcessWire\Pagefile;
33 | use ProcessWire\Pageimage;
34 | use ProcessWire\Pageimages;
35 |
36 | /**
37 | * Represents a single PDF file item attached to a page, typically via a FieldtypePDF field.
38 | */
39 | class PagePDF extends Pagefile
40 | {
41 | public static $defaultImageExtension = 'jpg';
42 |
43 | protected $options = array();
44 |
45 | protected $images;
46 |
47 | /**
48 | * Construct a new PagePDF
49 | *
50 | * @param PagePDFs $pagefiles Owning collection
51 | * @param string $filename Full path and filename to this pagefile
52 | */
53 | public function __construct(PagePDFs $pagefiles, $filename)
54 | {
55 | parent::__construct($pagefiles, $filename);
56 |
57 | $field = $pagefiles->getField();
58 | foreach($field->getArray() as $key => $value) {
59 | if (preg_match('/^converter(.+)$/', $key, $matches)) {
60 | $this->options[lcfirst($matches[1])] = $value;
61 | }
62 | }
63 |
64 | if ($field->get('imageExtension')) {
65 | $this->options['extension'] = $field->get('imageExtension');
66 | }
67 |
68 | if ($field->type->fallbackMode) {
69 | $this->options['fallbackMode'] = true;
70 | }
71 |
72 | $this->images = new Pageimages($this->pagefiles->getPage());
73 | }
74 |
75 | /**
76 | * Convert PDF to image
77 | *
78 | * This generates one image for each combinations of $page and $options['suffix'].
79 | * If the file already exists it isn't regenerated until $options['forceNew'] is TRUE.
80 | *
81 | * @param int $page Number of PDF's page (indexed from 0) to be converted to image
82 | * @param type $options
83 | *
84 | * Available options are:
85 | *
86 | * - sufix (string[]) - Suffixes to be used in image's filename
87 | * - forceNew (boolean) - Whether to overwrite the image if already exists
88 | *
89 | * Accepts also converter options, see {@link FieldtypePDF\PDFConverter::setOptions}.
90 | *
91 | * @return Pageimage
92 | */
93 | public function ___toImage($page = 0, $options = array())
94 | {
95 | if (is_array($page)) {
96 | $options = $page;
97 | $page = 0;
98 | }
99 |
100 | $defaultOptions = array(
101 | 'extension' => self::$defaultImageExtension,
102 | 'suffix' => array(),
103 | 'forceNew' => false,
104 | );
105 |
106 | if ($page > 0) {
107 | $defaultOptions['suffix'][] = 'page' . $page;
108 | }
109 |
110 | $options = array_replace($defaultOptions, $this->options, $options);
111 |
112 | $suffixStr = '';
113 | if(!empty($options['suffix'])) {
114 | $suffix = is_array($options['suffix']) ? $options['suffix'] : array($options['suffix']);
115 | sort($suffix);
116 | foreach($suffix as $key => $s) {
117 | $s = strtolower($this->wire('sanitizer')->fieldName($s));
118 | if(empty($s)) unset($suffix[$key]);
119 | else $suffix[$key] = $s;
120 | }
121 | if(count($suffix)) $suffixStr = '-' . implode('-', $suffix);
122 | }
123 |
124 | // e.g. myfile.pdf -> myfile-page2.jpg
125 | $basename = sprintf('%s%s.%s',
126 | basename($this->basename(), "." . $this->ext()),
127 | $suffixStr,
128 | $options['extension']
129 | );
130 |
131 | $filename = $this->pagefiles->path() . $basename;
132 | $exists = file_exists($filename);
133 |
134 | if(! $exists || $options['forceNew']) {
135 | if($exists && $options['forceNew']) {
136 | $image = new Pageimage($this->images, $filename);
137 | $image->unlink();
138 | }
139 |
140 | try {
141 | $converter = new PDFConverter($this->filename, $options);
142 | $converter->toImage($page, $filename);
143 |
144 | if($this->config->chmodFile) {
145 | chmod($filename, octdec($this->config->chmodFile));
146 | }
147 | } catch(Exception $e) {
148 | if ($this->pagefiles->getPage()->template === 'admin') {
149 | $this->error($e->getMessage());
150 | $this->error("PDF to image conversion failed for $filename");
151 | } else {
152 | throw $e;
153 | }
154 | }
155 | }
156 |
157 | $image = new Pageimage($this->images, $filename);
158 | $this->images->add($image);
159 | return $image;
160 | }
161 |
162 | /**
163 | * Test whether $basename is image generated from this PDF
164 | *
165 | * @param type $basename
166 | * @return boolean
167 | */
168 | public function isImageOfThis($basename)
169 | {
170 | $imageName = basename($basename);
171 | $originalName = basename($this->basename, "." . $this->ext()); // excludes extension
172 |
173 | $re = '/^'
174 | . $originalName // myfile
175 | . '(?:-([-_a-zA-Z0-9]+))?' // -suffix1 or -suffix1-suffix2, etc.
176 | . '\.[^.]+' // .jpg
177 | . '$/';
178 |
179 | // if regex does not match or file is PDF, return false
180 | if(! preg_match($re, $imageName) || preg_match('/^.*pdf$/', $imageName)) {
181 | return false;
182 | }
183 |
184 | return true;
185 | }
186 |
187 | /**
188 | * Get all images generated from this PDF
189 | *
190 | * @return Pageimages
191 | */
192 | public function getImages()
193 | {
194 | $images = new Pageimages($this->pagefiles->page);
195 | $dir = new DirectoryIterator($this->pagefiles->path);
196 |
197 | foreach($dir as $file) {
198 | if($file->isDir() || $file->isDot()) continue;
199 | if(! $this->isImageOfThis($file->getFilename())) continue;
200 | $images->add($file->getFilename());
201 | }
202 |
203 | return $images;
204 | }
205 |
206 | /**
207 | * Remove all generated images.
208 | *
209 | * @return void
210 | */
211 | public function removeImages()
212 | {
213 | $images = $this->getImages();
214 |
215 | foreach($images as $image) {
216 | $image->unlink();
217 | }
218 |
219 | return $this;
220 | }
221 |
222 | /**
223 | * Delete the physical file on disk associated with this PDF.
224 | *
225 | * Unlinks also all generated images.
226 | *
227 | * @return bool
228 | */
229 | public function unlink()
230 | {
231 | $this->removeImages();
232 | return parent::unlink();
233 | }
234 |
235 | /**
236 | * @deprecated since version 1.1.0, please use toImage() instead
237 | * @param int $width
238 | * @param int $height
239 | * @return Pageimage
240 | */
241 | public function thumbnail($width, $height = 0)
242 | {
243 | $height = $height ?: $width;
244 |
245 | $image = $this->toImage();
246 | return $image->size($width, $height);
247 | }
248 |
249 | /**
250 | * @deprecated since version 1.1.0, please use isImageOfThis() instead
251 | * @param string $basename
252 | */
253 | public function isThumbnail($basename)
254 | {
255 | $images = $this->getImages();
256 |
257 | if ($images->count() == 0) {
258 | $images->add($this->toImage());
259 | }
260 |
261 | foreach($images as $image) {
262 | if($image->basename === $basename || $image->isVariation($basename)) {
263 | return true;
264 | }
265 | }
266 |
267 | return false;
268 | }
269 |
270 | /**
271 | * @deprecated since version 1.1.0, please use removeImages() instead
272 | */
273 | public function removeThumbnails()
274 | {
275 | $this->removeImages();
276 | }
277 | }
278 |
--------------------------------------------------------------------------------
/FieldtypePDF/PagePDFs.php:
--------------------------------------------------------------------------------
1 | (http://uiii.cz)
7 | *
8 | * Permission is hereby granted, free of charge, to any person obtaining a copy
9 | * of this software and associated documentation files (the "Software"), to deal
10 | * in the Software without restriction, including without limitation the rights
11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | * copies of the Software, and to permit persons to whom the Software is
13 | * furnished to do so, subject to the following conditions:
14 | *
15 | * The above copyright notice and this permission notice shall be included in
16 | * all copies or substantial portions of the Software.
17 | *
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | * THE SOFTWARE.
25 | */
26 |
27 | namespace FieldtypePDF;
28 |
29 | use ProcessWire\Pagefiles;
30 |
31 | /**
32 | * PagePDFs are a collection of PagePDF objects.
33 | *
34 | * Typically a PagePDFs object will be associated with a specific field attached to a Page.
35 | * There may be multiple instances of PagePDFs attached to a given Page (depending on what fields are in it's fieldgroup).
36 | */
37 | class PagePDFs extends Pagefiles
38 | {
39 | public function isValidItem($item)
40 | {
41 | return $item instanceof PagePDF;
42 | }
43 |
44 | public function makeBlankItem()
45 | {
46 | return new PagePDF($this, '');
47 | }
48 |
49 | public function add($item)
50 | {
51 | if(is_string($item)) {
52 | $item = new PagePDF($this, $item);
53 | }
54 |
55 | return parent::add($item);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/InputfieldPDF.css:
--------------------------------------------------------------------------------
1 | .InputfieldPDF img {
2 | max-width: 90%;
3 | }
4 |
5 | .InputfieldPDF a.InputfieldFileLink + label.InputfieldFileDescription,
6 | .InputfieldPDF a.InputfieldFileLink + label.InputfieldFileTags {
7 | margin-top: 0.5em;
8 | }
9 |
10 | .InputfieldPDF a.InputfieldFileMove span.ui-icon {
11 | float: right;
12 | position: relative;
13 | top: 2px;
14 | }
15 | .InputfieldPDF a.InputfieldFileMove {
16 | float: right;
17 | width: 20px;
18 | padding-right: 20px;
19 | visibility: hidden;
20 | }
21 |
22 | .ui-state-hover a.InputfieldFileMove {
23 | visibility: visible;
24 | }
25 | .ui-state-error a.InputfieldFileMove {
26 | display: none;
27 | }
28 |
29 |
30 | /**
31 | * Grid mode
32 | *
33 | */
34 | .InputfieldPDF .InputfieldHeader i.icon-th,
35 | .InputfieldPDF .InputfieldHeader i.icon-list {
36 | font-size: 14px;
37 | }
38 |
39 | .InputfieldImageGrid .InputfieldContent {
40 | padding-right: 0;
41 | }
42 |
43 | .InputfieldImageGrid .InputfieldFileInfo {
44 | display: none;
45 | }
46 |
47 | .InputfieldImageGrid .AjaxUpload .InputfieldFileInfo {
48 | display: block;
49 | clear: both;
50 | float: none;
51 | margin-top: 1em;
52 | }
53 |
54 | .InputfieldImageGrid .InputfieldFileData label {
55 | display: none;
56 | }
57 |
58 | .InputfieldImageGrid .InputfieldFileList .langTabsContainer,
59 | .InputfieldImageGrid .InputfieldFileList .InputfieldFileDescription,
60 | .InputfieldImageGrid .InputfieldFileList .InputfieldFileTags {
61 | display: none !important;
62 | }
63 |
64 | .InputfieldImageGrid .InputfieldFileList .InputfieldImage,
65 | .InputfieldImageGrid .InputfieldFileList .InputfieldFileData {
66 | padding: 0;
67 | margin: 0;
68 | float: left;
69 | background: none;
70 | border: none;
71 | }
72 |
73 | .InputfieldImageGrid .InputfieldFileList .InputfieldFileData a.InputfieldFileLink {
74 | overflow: hidden;
75 | width: 100px;
76 | height: 100px;
77 | background: #000;
78 | margin: 0 5px 5px 0;
79 | display: block;
80 | float: left;
81 | cursor: move;
82 | }
83 | .InputfieldImageGrid .InputfieldFileList .InputfieldFileData a.InputfieldFileLink img {
84 | max-width: 9999px;
85 | width: auto;
86 | height: 100px;
87 | display: block;
88 | margin: 0 auto;
89 | display: none;
90 | }
91 |
92 | .Inputfields .InputfieldImageGrid > .InputfieldContent > .InputfieldFileList > li {
93 | margin: 0;
94 | }
95 |
96 | .InputfieldImageGrid .InputfieldFileUpload {
97 | padding-top: 1em;
98 | clear: both;
99 |
100 | }
101 |
102 | .InputfieldPDF .InputfieldHeader .InputfieldImageListToggle {
103 | float: right;
104 | padding-right: 1em;
105 | position: relative;
106 | }
107 | .InputfieldStateCollapsed .InputfieldHeader .InputfieldImageListToggle {
108 | display: none;
109 | }
110 |
111 |
--------------------------------------------------------------------------------
/InputfieldPDF.module.php:
--------------------------------------------------------------------------------
1 | (http://uiii.cz)
7 | *
8 | * Permission is hereby granted, free of charge, to any person obtaining a copy
9 | * of this software and associated documentation files (the "Software"), to deal
10 | * in the Software without restriction, including without limitation the rights
11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | * copies of the Software, and to permit persons to whom the Software is
13 | * furnished to do so, subject to the following conditions:
14 | *
15 | * The above copyright notice and this permission notice shall be included in
16 | * all copies or substantial portions of the Software.
17 | *
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | * THE SOFTWARE.
25 | */
26 |
27 | namespace ProcessWire;
28 |
29 | use FieldtypePDF\PagePDF;
30 |
31 | class InputfieldPDF extends InputfieldFile implements InputfieldItemList
32 | {
33 | public static function getModuleInfo()
34 | {
35 | return array(
36 | 'version' => 201,
37 | 'title' => __('PDF files with thumbnails', __FILE__), // Module Title
38 | 'summary' => __('One or more PDF files upload with thumbnails', __FILE__), // Module Summary
39 | 'href' => 'http://modules.processwire.com/modules/fieldtype-pdf',
40 | 'author' => "Richard Jedlička",
41 | 'requires' => array(
42 | 'ProcessWire>=3.0.0',
43 | 'FieldtypePDF'
44 | )
45 | );
46 | }
47 |
48 | public function init()
49 | {
50 | parent::init();
51 | $this->set('extensions', 'PDF');
52 | $this->set('adminThumbs', false);
53 |
54 | $options = $this->wire('config')->adminThumbOptions;
55 | if(!is_array($options)) $options = array();
56 | if(empty($options['width']) && empty($options['height'])) $options['height'] = 100; // default
57 | $this->set('adminThumbWidth', empty($options['width']) ? 0 : (int) $options['width']);
58 | $this->set('adminThumbHeight', empty($options['height']) ? 0 : (int) $options['height']);
59 | $this->set('adminThumbScale', empty($options['scale']) ? 1.0 : (float) $options['scale']);
60 | }
61 |
62 | public function getAdminThumb(PagePDF $pdf)
63 | {
64 | $thumb = $pdf->toImage();
65 | $thumbInfo = array();
66 |
67 | /** @var InputfieldImage */
68 | $inputfieldImage = $this->modules->get('InputfieldImage');
69 | if (method_exists($inputfieldImage, 'getAdminThumb')) { // PW 2.6+
70 | $inputfieldImage->set('adminThumbs', true);
71 | return $inputfieldImage->getAdminThumb($pdf->toImage());
72 | } else {
73 | $error = '';
74 | $thumbAttrs = array();
75 |
76 | if($this->adminThumbs) {
77 | $thumbHeight = $thumb->height;
78 | if($thumbHeight > $this->adminThumbHeight) {
79 | // create a variation for display with this inputfield
80 | $thumb = $thumb->height($this->adminThumbHeight);
81 | if($thumb->error) $error = "$thumb->error";
82 | $thumbHeight = $this->adminThumbHeight;
83 | }
84 | $thumbAttrs['height'] = $thumbHeight;
85 | $thumbAttrs['width'] = $thumb->width;
86 | }
87 |
88 | $thumbAttrs['src'] = $thumb->url;
89 |
90 | // ensure cached image doesn't get shown when replacing same filename
91 | if($this->overwrite) $thumbAttrs['src'] .= "?m=" . filemtime($pdf->pathname);
92 |
93 | $thumbInfo['attr'] = $thumbAttrs;
94 | $thumbInfo['error'] = $error;
95 |
96 | $markup = " $value) $markup .= "$key=\"$value\" ";
98 | $markup .= " />";
99 |
100 | $thumbInfo['markup'] = $markup;
101 | }
102 |
103 | $thumbInfo['thumb'] = $thumb;
104 |
105 | return $thumbInfo;
106 | }
107 |
108 |
109 | public function ___render() {
110 | $this->wire('modules')->loadModuleFileAssets('InputfieldFile');
111 |
112 | return parent::___render();
113 | }
114 |
115 | protected function ___renderItem($pagefile, $id, $n)
116 | {
117 | $thumb = $this->getAdminThumb($pagefile);
118 | $error = $thumb['error'] ? "" . $this->wire('sanitizer')->entities($thumb['error']) . "" : "";
119 |
120 | $out = "\n\t\t