|
From: <mi...@us...> - 2023-06-30 13:59:42
|
Revision: 9423
http://sourceforge.net/p/docutils/code/9423
Author: milde
Date: 2023-06-30 13:59:40 +0000 (Fri, 30 Jun 2023)
Log Message:
-----------
Revise image reading in HTML writers.
The HTML writers read image files for embedded images
and to determine the size of images that should be scaled.
* Use urllib to parse image URI (support for "file:" scheme).
* Warn, if scaling fails because the image cannot be read.
Adapt "record dependencies" test to suppress warning
if PIL is not installed.
Modified Paths:
--------------
trunk/docutils/HISTORY.txt
trunk/docutils/docutils/writers/_html_base.py
trunk/docutils/test/test_dependencies.py
Modified: trunk/docutils/HISTORY.txt
===================================================================
--- trunk/docutils/HISTORY.txt 2023-06-29 13:50:25 UTC (rev 9422)
+++ trunk/docutils/HISTORY.txt 2023-06-30 13:59:40 UTC (rev 9423)
@@ -75,7 +75,11 @@
- Stop setting the "footnote-reference" class value for footnote references.
Since 0.18, you can use the CSS selector ``[role="doc-noteref"]``.
+ - Support reading/embedding images with "file:" URIs.
+ - Warn, if image scaling fails because the image file cannot be read.
+
+
Release 0.20.1 (2023-05-17)
===========================
Modified: trunk/docutils/docutils/writers/_html_base.py
===================================================================
--- trunk/docutils/docutils/writers/_html_base.py 2023-06-29 13:50:25 UTC (rev 9422)
+++ trunk/docutils/docutils/writers/_html_base.py 2023-06-30 13:59:40 UTC (rev 9423)
@@ -21,7 +21,7 @@
import os
import os.path
import re
-from urllib.request import unquote as unquote_url
+import urllib
from urllib.request import url2pathname # unquote and use local path sep
import warnings
@@ -1008,7 +1008,10 @@
def visit_image(self, node):
atts = {}
uri = node['uri']
+ uri_parts = urllib.parse.urlparse(uri)
+ imagepath = ''
mimetype = mimetypes.guess_type(uri)[0]
+ scaling_problems = []
# image size
if 'width' in node:
atts['width'] = node['width']
@@ -1015,22 +1018,34 @@
if 'height' in node:
atts['height'] = node['height']
if 'scale' in node:
- if (PIL and ('width' not in node or 'height' not in node)
- and self.settings.file_insertion_enabled):
- imagepath = url2pathname(uri)
- try:
- with PIL.Image.open(imagepath) as img:
- imgsize = img.size
- except (OSError, UnicodeEncodeError):
- pass # TODO: warn?
+ if 'width' not in node or 'height' not in node:
+ # try reading size from image file
+ if uri_parts.scheme not in ('', 'file'):
+ scaling_problems.append('Works only for local images.')
+ if not PIL:
+ scaling_problems.append('Requires Python Imaging Library.')
+ if not self.settings.file_insertion_enabled:
+ scaling_problems.append('Reading external files disabled.')
+ if not scaling_problems:
+ imagepath = url2pathname(uri_parts.path)
+ try:
+ with PIL.Image.open(imagepath) as img:
+ imgsize = img.size
+ except (OSError, UnicodeEncodeError) as err:
+ scaling_problems.append(str(err))
+ else:
+ self.settings.record_dependencies.add(
+ imagepath.replace('\\', '/'))
+ if scaling_problems:
+ self.document.reporter.warning(
+ '\n '.join([f'Cannot scale "{imagepath or uri}".']
+ + scaling_problems))
else:
- self.settings.record_dependencies.add(
- imagepath.replace('\\', '/'))
if 'width' not in atts:
atts['width'] = '%dpx' % imgsize[0]
if 'height' not in atts:
atts['height'] = '%dpx' % imgsize[1]
- del img
+ # scale provided/determined size values:
for att_name in 'width', 'height':
if att_name in atts:
match = re.match(r'([0-9.]+)(\S*)$', atts[att_name])
@@ -1059,6 +1074,16 @@
atts['class'] = 'align-%s' % node['align']
# Embed image file (embedded SVG or data URI):
if self.image_loading == 'embed':
+ if uri_parts.scheme not in ('', 'file'):
+ self.document.reporter.error(
+ f'Cannot embed remote image "{uri}"')
+ # TODO: read with urllib.request?
+ imagepath = imagepath or url2pathname(uri_parts.path)
+ # TODO: adapt relative imagepath?
+ # if not os.path.isabs(imagepath):
+ # _src, _ln = utils.get_source_line(node)
+ # dirname = os.path.dirname(_src or '')
+ # imagepath = os.path.join(dirname, imagepath)
try:
with open(url2pathname(uri), 'rb') as imagefile:
imagedata = imagefile.read()
@@ -1066,7 +1091,8 @@
self.document.reporter.error('Cannot embed image %r: %s'
% (uri, err.strerror))
else:
- self.settings.record_dependencies.add(unquote_url(uri))
+ self.settings.record_dependencies.add(
+ urllib.parse.unquote(uri))
# TODO: insert SVG as-is?
# if mimetype == 'image/svg+xml':
# read/parse, apply arguments,
Modified: trunk/docutils/test/test_dependencies.py
===================================================================
--- trunk/docutils/test/test_dependencies.py 2023-06-29 13:50:25 UTC (rev 9422)
+++ trunk/docutils/test/test_dependencies.py 2023-06-30 13:59:40 UTC (rev 9423)
@@ -78,7 +78,7 @@
expected = [paths[key] for key in keys]
record, output = self.get_record(writer_name='xml')
# the order of the files is arbitrary
- self.assertEqual(sorted(record), sorted(expected))
+ self.assertEqual(sorted(expected), sorted(record))
def test_dependencies_html(self):
keys = ['include', 'raw']
@@ -86,11 +86,13 @@
keys += ['figure-image', 'scaled-image']
expected = [paths[key] for key in keys]
# stylesheets are tested separately in test_stylesheet_dependencies():
- settings = {'stylesheet_path': None, 'stylesheet': None}
+ settings = {'stylesheet_path': None,
+ 'stylesheet': None,
+ 'report_level': 4} # drop warning if PIL is missing
record, output = self.get_record(writer_name='html5',
settings_overrides=settings)
# the order of the files is arbitrary
- self.assertEqual(sorted(record), sorted(expected),
+ self.assertEqual(sorted(expected), sorted(record),
msg='output is:\n'+output)
def test_dependencies_latex(self):
@@ -106,13 +108,13 @@
writer_name='latex',
settings_overrides=latex_settings_overwrites)
# the order of the files is arbitrary
- self.assertEqual(sorted(record), sorted(expected),
+ self.assertEqual(sorted(expected), sorted(record),
msg='output is:\n'+output)
def test_csv_dependencies(self):
csvsource = str(DATA_ROOT / 'csv_dep.txt')
record, output = self.get_record(source_path=csvsource)
- self.assertEqual(record, [relpath(DATA_ROOT / 'csv_data.txt')],
+ self.assertEqual([relpath(DATA_ROOT / 'csv_data.txt')], record,
msg='output is:\n'+output)
def test_stylesheet_dependencies(self):
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|