sub
new {
my
(
$class
,
%options
) =
@_
;
my
$self
=
$class
->SUPER::new(
%options
);
$$self
{DPI} =
$options
{DPI};
$$self
{ignore_options} =
$options
{ignore_options} || [];
$$self
{trivial_scaling} =
$options
{trivial_scaling} || 1;
$$self
{graphics_types} =
$options
{graphics_types}
|| [
qw(svg png gif jpg jpeg
eps ps postscript ai pdf)
];
$$self
{type_properties} =
$options
{type_properties}
|| {
ai
=> {
destination_type
=>
'png'
,
transparent
=> 1,
prescale
=> 1,
ncolors
=>
'400%'
,
quality
=> 90,
unit
=>
'point'
},
pdf
=> {
destination_type
=>
'png'
,
transparent
=> 1,
prescale
=> 1,
ncolors
=>
'400%'
,
quality
=> 90,
unit
=>
'point'
},
ps
=> {
destination_type
=>
'png'
,
transparent
=> 1,
prescale
=> 1,
ncolors
=>
'400%'
,
quality
=> 90,
unit
=>
'point'
},
eps
=> {
destination_type
=>
'png'
,
transparent
=> 1,
prescale
=> 1,
ncolors
=>
'400%'
,
quality
=> 90,
unit
=>
'point'
},
jpg
=> {
destination_type
=>
'jpg'
,
ncolors
=>
'400%'
,
unit
=>
'pixel'
},
jpeg
=> {
destination_type
=>
'jpeg'
,
ncolors
=>
'400%'
,
unit
=>
'pixel'
},
gif
=> {
destination_type
=>
'gif'
,
transparent
=> 1,
ncolors
=>
'400%'
,
unit
=>
'pixel'
},
png
=> {
destination_type
=>
'png'
,
transparent
=> 1,
ncolors
=>
'400%'
,
unit
=>
'pixel'
},
svg
=> {
destination_type
=>
'svg'
,
raster
=> 0,
desirability
=> 11 },
};
$$self
{background} =
$options
{background} ||
"#FFFFFF"
;
return
$self
; }
sub
toProcess {
my
(
$self
,
$doc
) =
@_
;
return
$doc
->findnodes(
'//ltx:graphics[not(@imagesrc)]'
); }
sub
process {
my
(
$self
,
$doc
,
@nodes
) =
@_
;
local
$LaTeXML::Post::Graphics::SEARCHPATHS
= [
map
{ pathname_canonical(
$_
) }
$self
->findGraphicsPaths(
$doc
),
$doc
->getSearchPaths];
Debug(
" [Using graphicspaths: "
.
join
(
', '
,
@$LaTeXML::Post::Graphics::SEARCHPATHS
) .
"]"
)
if
$LaTeXML::DEBUG
{images};
foreach
my
$node
(
@nodes
) {
$self
->processGraphic(
$doc
,
$node
); }
$doc
->closeCache;
return
$doc
; }
sub
getParameter {
my
(
$self
,
$doc
,
$parameter
) =
@_
;
my
$value
;
foreach
my
$pi
(@{
$$doc
{processingInstructions} }) {
if
(
$pi
=~ /^\s
*$parameter
\s*=\s*([\"\'])(.*?)\1\s*$/) {
$value
= $2; }
elsif
(
$pi
=~ /^\s
*package
\s*=\s*([\"\'])latexml\1\s
*options
\s*=\s*([\"\'])(.*?)\2\s*$/i) {
my
$options
= $3;
if
(
$options
=~ /\b
$parameter
\s*=\s*(\d+)\b/i) {
$value
= $1; } } }
return
$value
||
$$self
{
$parameter
}; }
sub
findGraphicsPaths {
my
(
$self
,
$doc
) =
@_
;
my
@paths
= ();
foreach
my
$pi
(
$doc
->findnodes(
'.//processing-instruction("latexml")'
)) {
if
(
$pi
->textContent =~ /^\s
*graphicspath
\s*=\s*([\"\'])(.*?)\1\s*$/) {
push
(
@paths
, $2); } }
return
@paths
; }
sub
getGraphicsSourceTypes {
my
(
$self
) =
@_
;
return
@{
$$self
{graphics_types} },
map
{
uc
(
$_
); } @{
$$self
{graphics_types} }; }
sub
findGraphicFile {
my
(
$self
,
$doc
,
$node
) =
@_
;
if
(
my
$source
=
$node
->getAttribute(
'graphic'
)) {
my
@paths
=
grep
{ -e
$_
}
split
(
','
,
$node
->getAttribute(
'candidates'
) ||
''
);
if
(!
scalar
(
@paths
)) {
my
(
$dir
,
$name
,
$reqtype
) = pathname_split(
$source
);
if
((
length
(
$reqtype
) > 0) && !(
grep
{
$_
eq
lc
(
$reqtype
) }
$$self
{graphics_types})) {
$name
.=
".$reqtype"
; }
my
$file
= pathname_concat(
$dir
,
$name
);
@paths
= pathname_findall(
$file
,
paths
=>
$LaTeXML::Post::Graphics::SEARCHPATHS
,
types
=> [
''
,
$self
->getGraphicsSourceTypes]);
}
my
(
$best
,
$bestpath
) = (-1,
undef
);
foreach
my
$path
(
@paths
) {
my
$type
= pathname_type(
$path
);
my
$props
=
$$self
{type_properties}{
$type
};
my
$desirability
=
$$props
{desirability} || (
$type
eq (
$$props
{destination_type} ||
'notype'
) ? 10 : 0);
if
(
$desirability
>
$best
) {
$best
=
$desirability
;
$bestpath
=
$path
; } }
return
$bestpath
; }
else
{
return
; } }
sub
getTransform {
my
(
$self
,
$node
) =
@_
;
my
$options
=
$node
->getAttribute(
'options'
);
return
(
$options
? image_graphicx_parse(
$options
) : []); }
sub
getTypeProperties {
my
(
$self
,
$source
,
$options
) =
@_
;
my
(
$dir
,
$name
,
$ext
) = pathname_split(
$source
);
my
$props
=
$$self
{type_properties}{
lc
(
$ext
) };
if
(!
$props
) {
if
(
my
$type
= image_type(
$source
)) {
$props
=
$$self
{type_properties}{
lc
(
$type
) }; } }
return
(
$props
?
%$props
: ()); }
sub
setGraphicSrc {
my
(
$self
,
$node
,
$src
,
$width
,
$height
) =
@_
;
$node
->setAttribute(
'imagesrc'
, pathname_to_url(
$src
));
if
(
defined
$width
) {
$width
=
int
(
$width
)
if
(
$width
=~ /^\d*\.\d*$/);
$node
->setAttribute(
'imagewidth'
,
$width
); }
if
(
defined
$height
) {
$height
=
int
(
$height
)
if
(
$height
=~ /^\d*\.\d*$/);
$node
->setAttribute(
'imageheight'
,
$height
); }
if
(
$width
and
$height
) {
my
$aspect_class
=
"ltx_img_square"
;
if
(
$width
> 1.24 *
$height
) {
$aspect_class
=
"ltx_img_landscape"
; }
elsif
(
$height
> 1.24 *
$width
) {
$aspect_class
=
"ltx_img_portrait"
}
my
$class
=
$node
->getAttribute(
'class'
);
$class
= (
$class
?
$class
.
' '
:
''
) .
$aspect_class
;
$node
->setAttribute(
'class'
,
$class
); }
return
; }
sub
processGraphic {
my
(
$self
,
$doc
,
$node
) =
@_
;
my
$source
=
$self
->findGraphicFile(
$doc
,
$node
);
if
(!
$source
) {
Warn(
'expected'
,
'source'
,
$node
,
"No graphic source found; skipping"
,
"source was "
. (
$node
->getAttribute(
'graphic'
) ||
'none'
));
$node
->setAttribute(
'imagesrc'
,
$node
->getAttribute(
'graphic'
));
return
; }
my
$transform
=
$self
->getTransform(
$node
);
my
(
$image
,
$width
,
$height
) =
$self
->transformGraphic(
$doc
,
$node
,
$source
,
$transform
);
$self
->setGraphicSrc(
$node
, pathname_relative(
$image
,
$doc
->getDestinationDirectory),
$width
,
$height
)
if
$image
;
return
; }
sub
transformGraphic {
my
(
$self
,
$doc
,
$node
,
$source
,
$transform
) =
@_
;
my
@parameters
= (
DPI
=>
$self
->getParameter(
$doc
,
'DPI'
) ||
$LaTeXML::Util::Image::DPI
,
magnify
=>
$self
->getParameter(
$doc
,
'magnify'
),
upsample
=>
$self
->getParameter(
$doc
,
'upsample'
),
zoomout
=>
$self
->getParameter(
$doc
,
'zoomout'
));
my
$sourcedir
=
$doc
->getSourceDirectory;
(
$sourcedir
) =
$doc
->getSearchPaths
unless
$sourcedir
;
my
(
$reldir
,
$name
,
$srctype
)
= pathname_split(pathname_relative(
$source
,
$sourcedir
));
my
%properties
=
$self
->getTypeProperties(
$source
,
$transform
);
return
Warn(
'unexpected'
,
'graphics_format'
,
undef
,
"Don't know what to do with graphics file format '$source'"
)
unless
%properties
;
my
$type
=
$properties
{destination_type} ||
$srctype
;
my
$key
= (
ref
$self
) .
':'
.
join
(
'|'
,
"$reldir$name.$srctype.$type"
,
map
{
join
(
' '
,
@$_
) }
@$transform
);
Debug(
"Processing $source as key=$key"
)
if
$LaTeXML::DEBUG
{images};
my
$dest
=
$self
->desiredResourcePathname(
$doc
,
$node
,
$source
,
$type
);
if
(
my
$prev
=
$doc
->cacheLookup(
$key
)) {
if
(
$prev
=~ /^(.*?)\|(\d*)\|(\d*)$/) {
my
(
$cached
,
$width
,
$height
) = ($1, $2, $3);
$width
=
undef
unless
$width
;
$height
=
undef
unless
$height
;
if
((!
defined
$dest
) || (
$cached
eq
$dest
)) {
my
$absdest
= pathname_absolute(
$cached
,
$doc
->getDestinationDirectory);
if
(pathname_timestamp(
$source
) <= pathname_timestamp(
$absdest
)) {
Debug(
" [Reuse $cached @ "
. (
$width
||
'?'
) .
" x "
. (
$height
||
'?'
) .
"]"
)
if
$LaTeXML::DEBUG
{images};
return
(
$cached
,
$width
,
$height
); } } } }
my
$triv_scaling
=
$$self
{trivial_scaling} && (
$type
eq
$srctype
)
&& image_graphicx_is_trivial(
$transform
);
if
(!
$triv_scaling
&& (
defined
$properties
{raster}) && !
$properties
{raster}) {
Warn(
"limitation"
,
$source
,
undef
,
"Cannot (yet) apply complex transforms to non-raster images"
,
join
(
','
,
map
{
join
(
' '
,
@$_
) }
grep
{ !(
$_
->[0] =~ /^scale/) }
@$transform
));
$triv_scaling
= 1;
$transform
= image_graphicx_trivialize(
$transform
); }
if
(!image_can_image()) {
if
(
$type
ne
$srctype
) {
Error(
'imageprocessing'
,
'imageclass'
,
undef
,
"No image processing module found to convert types"
,
"Skipping $source=>$type."
,
"Please install one of: "
.
join
(
','
, image_classes()));
return
; }
elsif
(!
$triv_scaling
) {
Error(
'imageprocessing'
,
'imageclass'
,
undef
,
"No image processing module found for complex transformations"
,
"Simplifying transformation of $source."
,
"Please install one of: "
.
join
(
','
, image_classes()));
$triv_scaling
= 1;
$transform
= image_graphicx_trivialize(
$transform
); }
}
my
(
$image
,
$width
,
$height
);
if
(
$triv_scaling
) {
$dest
= pathname_relative(
$source
,
$doc
->getSourceDirectory);
my
$absdest
= pathname_absolute(
$dest
,
$doc
->getDestinationDirectory);
if
(!pathname_is_contained(
$absdest
,
$doc
->getSiteDirectory)) {
$dest
=
$self
->generateResourcePathname(
$doc
,
$node
,
$source
,
$type
);
$absdest
=
$doc
->checkDestination(
$dest
); }
Debug(
" [Destination $absdest]"
)
if
$LaTeXML::DEBUG
{images};
(
$width
,
$height
) = image_graphicx_trivial(
$source
,
$transform
,
@parameters
);
if
(!(
$width
&&
$height
)) {
if
(!image_can_image()) {
Warn(
'imageprocessing'
,
'imagesize'
,
undef
,
"No image processing module found for image sizing"
,
"Will omit image size for $source"
,
"Please install one of: "
.
join
(
','
, image_classes())); }
else
{
Warn(
'expected'
,
'image'
,
undef
,
"Couldn't get usable image size for $source"
); } }
pathname_copy(
$source
,
$absdest
)
or Warn(
'I/O'
,
$absdest
,
undef
,
"Couldn't copy $source to $absdest"
,
"Response was: $!"
);
Debug(
" [Copied to $dest @ "
. (
$width
||
'?'
) .
" x "
. (
$height
||
'?'
) .
"]"
)
if
$LaTeXML::DEBUG
{images}; }
else
{
$dest
=
$self
->generateResourcePathname(
$doc
,
$node
,
$source
,
$type
)
unless
$dest
;
my
$absdest
=
$doc
->checkDestination(
$dest
);
Debug(
" [Destination $absdest]"
)
if
$LaTeXML::DEBUG
{images};
(
$image
,
$width
,
$height
) = image_graphicx_complex(
$source
,
$transform
,
@parameters
,
background
=>
$$self
{background},
%properties
);
if
(!(
$image
&&
$width
&&
$height
)) {
Warn(
'expected'
,
'image'
,
undef
,
"Couldn't get usable image for $source"
);
return
; }
Debug(
" [Writing to $absdest]"
)
if
$LaTeXML::DEBUG
{images};
image_write(
$image
,
$absdest
) or
return
; }
$doc
->cacheStore(
$key
,
"$dest|"
. (
$width
||
''
) .
'|'
. (
$height
||
''
));
Debug(
" [done with $key]"
)
if
$LaTeXML::DEBUG
{images};
return
(
$dest
,
$width
,
$height
); }
1;