This commit is contained in:
Andy Bunce 2017-04-30 10:39:12 +01:00
parent f2bd581fe4
commit d25d32798a
40 changed files with 475 additions and 84 deletions

View File

@ -1,6 +1,6 @@
<xqdoc:xqdoc xmlns:xqdoc="http://www.xqdoc.org/1.0">
<xqdoc:control>
<xqdoc:date>2017-04-21T15:03:58.643+01:00</xqdoc:date>
<xqdoc:date>2017-04-29T22:28:45.705+01:00</xqdoc:date>
<xqdoc:version>1.1</xqdoc:version>
</xqdoc:control>
<xqdoc:module type="library">
@ -10,20 +10,47 @@
<xqdoc:description>Generate image thumbnails using the thumbnailator library.</xqdoc:description>
<xqdoc:see>https://github.com/coobird/thumbnailator</xqdoc:see>
<xqdoc:author>andy bunce</xqdoc:author>
<xqdoc:version>4.4.1</xqdoc:version>
<xqdoc:version>0.5</xqdoc:version>
</xqdoc:comment>
</xqdoc:module>
<xqdoc:namespaces>
<xqdoc:namespace prefix="thumbnails" uri="expkg-zone58:image.thumbnailator"/>
<xqdoc:namespace prefix="Thumbs" uri="org.expkgzone58.image.Thumbs"/>
</xqdoc:namespaces>
<xqdoc:imports/>
<xqdoc:imports>
<xqdoc:import type="library">
<xqdoc:uri>org.expkgzone58.image.Thumbs</xqdoc:uri>
</xqdoc:import>
</xqdoc:imports>
<xqdoc:variables/>
<xqdoc:functions>
<xqdoc:function arity="2">
<xqdoc:comment>
<xqdoc:description>generate scaled version of source image with maximum dimension of size</xqdoc:description>
<xqdoc:param>$source base64Binary (streamed?) e.g from `fetch:binary`</xqdoc:param>
<xqdoc:return>base64Binary for thumbnail</xqdoc:return>
</xqdoc:comment>
<xqdoc:name>thumbnails:size</xqdoc:name>
<xqdoc:signature>declare function thumbnails:size($source as xs:base64Binary, $size as xs:integer) as xs:base64Binary</xqdoc:signature>
<xqdoc:parameters>
<xqdoc:parameter>
<xqdoc:name>source</xqdoc:name>
<xqdoc:type>xs:base64Binary</xqdoc:type>
</xqdoc:parameter>
<xqdoc:parameter>
<xqdoc:name>size</xqdoc:name>
<xqdoc:type>xs:integer</xqdoc:type>
</xqdoc:parameter>
</xqdoc:parameters>
<xqdoc:return>
<xqdoc:type>xs:base64Binary</xqdoc:type>
</xqdoc:return>
</xqdoc:function>
<xqdoc:function arity="3">
<xqdoc:comment>
<xqdoc:description>generate scaled version of source image with maximum dimension of size</xqdoc:description>
<xqdoc:param>$source base64Binary (streamed?) e.g from `fetch:binary`</xqdoc:param>
<xqdoc:custom tag="result">base64Binary for thumbnail</xqdoc:custom>
<xqdoc:return>base64Binary for thumbnail</xqdoc:return>
</xqdoc:comment>
<xqdoc:name>thumbnails:size</xqdoc:name>
<xqdoc:signature>declare function thumbnails:size($source as xs:base64Binary, $width as xs:integer, $height as xs:integer) as xs:base64Binary</xqdoc:signature>
@ -45,14 +72,36 @@
<xqdoc:type>xs:base64Binary</xqdoc:type>
</xqdoc:return>
</xqdoc:function>
<xqdoc:function arity="2">
<xqdoc:comment>
<xqdoc:description>generate scaled version of source image at given factors 0-1</xqdoc:description>
<xqdoc:param>$source base64Binary (streamed?) e.g from `fetch:binary`</xqdoc:param>
<xqdoc:return>the thumbnail</xqdoc:return>
</xqdoc:comment>
<xqdoc:name>thumbnails:scale</xqdoc:name>
<xqdoc:signature>declare function thumbnails:scale($source as xs:base64Binary, $scale as xs:double) as xs:base64Binary</xqdoc:signature>
<xqdoc:parameters>
<xqdoc:parameter>
<xqdoc:name>source</xqdoc:name>
<xqdoc:type>xs:base64Binary</xqdoc:type>
</xqdoc:parameter>
<xqdoc:parameter>
<xqdoc:name>scale</xqdoc:name>
<xqdoc:type>xs:double</xqdoc:type>
</xqdoc:parameter>
</xqdoc:parameters>
<xqdoc:return>
<xqdoc:type>xs:base64Binary</xqdoc:type>
</xqdoc:return>
</xqdoc:function>
<xqdoc:function arity="3">
<xqdoc:comment>
<xqdoc:description>generate scaled version of source image at given factors 0-1</xqdoc:description>
<xqdoc:param>$source base64Binary (streamed?) e.g from `fetch:binary`</xqdoc:param>
<xqdoc:custom tag="result">base64Binary for thumbnail</xqdoc:custom>
<xqdoc:return>the thumbnail</xqdoc:return>
</xqdoc:comment>
<xqdoc:name>thumbnails:scale</xqdoc:name>
<xqdoc:signature>declare function thumbnails:scale($source as xs:base64Binary, $xscale as xs:float, $yscale as xs:float) as xs:base64Binary</xqdoc:signature>
<xqdoc:signature>declare function thumbnails:scale($source as xs:base64Binary, $xscale as xs:double, $yscale as xs:double) as xs:base64Binary</xqdoc:signature>
<xqdoc:parameters>
<xqdoc:parameter>
<xqdoc:name>source</xqdoc:name>
@ -60,11 +109,11 @@
</xqdoc:parameter>
<xqdoc:parameter>
<xqdoc:name>xscale</xqdoc:name>
<xqdoc:type>xs:float</xqdoc:type>
<xqdoc:type>xs:double</xqdoc:type>
</xqdoc:parameter>
<xqdoc:parameter>
<xqdoc:name>yscale</xqdoc:name>
<xqdoc:type>xs:float</xqdoc:type>
<xqdoc:type>xs:double</xqdoc:type>
</xqdoc:parameter>
</xqdoc:parameters>
<xqdoc:return>
@ -76,7 +125,7 @@
<xqdoc:description>generate thumbnail using parameters specified via XML</xqdoc:description>
<xqdoc:param>$source base64Binary (streamed?) e.g from `fetch:binary`</xqdoc:param>
<xqdoc:param>$task XML parameters &lt;task&gt;&lt;size width="100" ..</xqdoc:param>
<xqdoc:custom tag="result">base64Binary for thumbnail</xqdoc:custom>
<xqdoc:return>the thumbnail</xqdoc:return>
</xqdoc:comment>
<xqdoc:name>thumbnails:task</xqdoc:name>
<xqdoc:signature>declare function thumbnails:task($source as xs:base64Binary, $task as element(thumbnail)) as xs:base64Binary</xqdoc:signature>
@ -96,9 +145,31 @@
</xqdoc:function>
<xqdoc:function arity="1">
<xqdoc:comment>
<xqdoc:description>validate task XML against schema</xqdoc:description>
<xqdoc:param>$src XML parameters &lt;task&gt;&lt;size width="100" ..</xqdoc:param>
<xqdoc:custom tag="result">validation report</xqdoc:custom>
<xqdoc:description>validate task thumbnail XML against schema</xqdoc:description>
<xqdoc:param>$src XML parameters &lt;thumbnail&gt;&lt;size width="100" ..</xqdoc:param>
<xqdoc:return>empty-sequence or error</xqdoc:return>
<xqdoc:error>BXVA0001: the validation fails.</xqdoc:error>
<xqdoc:error>BXVA0002: the validation process cannot be started.</xqdoc:error>
<xqdoc:error>BXVA0003: no XML Schema validator is available.</xqdoc:error>
<xqdoc:error>BXVA0004: no validator is found for the specified version.</xqdoc:error>
</xqdoc:comment>
<xqdoc:name>thumbnails:validate</xqdoc:name>
<xqdoc:signature>declare function thumbnails:validate($src as item()*) as empty-sequence()</xqdoc:signature>
<xqdoc:parameters>
<xqdoc:parameter>
<xqdoc:name>src</xqdoc:name>
<xqdoc:type occurrence="*">item()</xqdoc:type>
</xqdoc:parameter>
</xqdoc:parameters>
<xqdoc:return>
<xqdoc:type>empty-sequence()</xqdoc:type>
</xqdoc:return>
</xqdoc:function>
<xqdoc:function arity="1">
<xqdoc:comment>
<xqdoc:description>validate task thumbnail XML against schema</xqdoc:description>
<xqdoc:param>$src XML parameters &lt;thumbnail&gt;&lt;size width="100" ..</xqdoc:param>
<xqdoc:return>validation report</xqdoc:return>
</xqdoc:comment>
<xqdoc:name>thumbnails:validation-report</xqdoc:name>
<xqdoc:signature>declare function thumbnails:validation-report($src as item()*) as element(report)</xqdoc:signature>

BIN
doc/constrain/A17057.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 KiB

View File

@ -0,0 +1,29 @@
(:~
:examples of constrain use
:)
import module namespace t="expkg-zone58:image.thumbnailator";
declare variable $file-base:=file:parent(static-base-uri());
declare variable $src:=file:resolve-path("A17057.jpg",$file-base);
declare function local:wi($data as xs:base64Binary,$filename as xs:string)
{
file:write-binary(file:resolve-path($filename,$file-base),$data)
};
declare function local:constrain($aspect as xs:boolean,$fit as xs:boolean,$exif as xs:boolean)
{
<thumbnail>
<size width="60" height="200"/>
<constrain aspect="{$aspect}" fit="{$fit}" exif="{$exif}"/>
</thumbnail>
};
let $s:=function($b){if($b) then ".+" else ".-"}
let $img:= fetch:binary($src)
let $ft:=(false(),true())
for $fit in $ft,$aspect in $ft,$exif in $ft
let $file:=("out.",
$s($aspect) , "aspect",
$s($fit), "fit",
$s($exif), "exif",
".jpg")=>string-join("")
let $task:=local:constrain($aspect,$fit,$exif)
return t:task($img,$task) => local:wi($file)

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
doc/filters-schema.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

32
doc/readme.md Normal file
View File

@ -0,0 +1,32 @@
# expkg-zone58:image.thumbnailator
The schema is [task.xsd](../src/main/content/task.xsd)
## thumbnail node
![root](thumbnail-schema.png "thumbnail node")
Either `size` or `scale` must be specified. Everything else is optional
`output` can be used to change the output image format.
```
<output format="gif"/>
```
## Filters
![filter definition](filters-schema.png "Available filters")
## Exif
Image handling is effected by the presence of the
[exif-orientation](http://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/) tag.
In particular:
* `size` and `fit` are changed
* the `flip` filter is ignored
To prevent this set constrain @exif=false
```
<constrain exif="false"/>
```

BIN
doc/thumbnail-schema.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="WINDOWS-1252" standalone="no"?>
<jardesc>
<jar path="ex-thumbnailator/src/main/content/thumbhelper-0.4.17.jar"/>
<jar path="ex-thumbnailator/src/main/content/thumbhelper-5.0.11.jar"/>
<options buildIfNeeded="true" compress="true" descriptionLocation="/ex-thumbnailator/makejar.jardesc" exportErrors="true" exportWarnings="true" includeDirectoryEntries="false" overwrite="true" saveDescription="true" storeRefactorings="false" useSourceFolders="false"/>
<storedRefactorings deprecationInfo="true" structuralOnly="false"/>
<selectedProjects/>

View File

@ -16,5 +16,38 @@
<version num="4.1.5">
<!-- generated: {fn:current-dateTime()} -->
</version>
<version num="5.0.0">
<!-- generated: {fn:current-dateTime()} -->
</version>
<version num="5.0.1">
<!-- generated: {fn:current-dateTime()} -->
</version>
<version num="5.0.2">
<!-- generated: {fn:current-dateTime()} -->
</version>
<version num="5.0.3">
<!-- generated: {fn:current-dateTime()} -->
</version>
<version num="5.0.4">
<!-- generated: {fn:current-dateTime()} -->
</version>
<version num="5.0.5">
<!-- generated: {fn:current-dateTime()} -->
</version>
<version num="5.0.7">
<!-- generated: {fn:current-dateTime()} -->
</version>
<version num="5.0.8">
<!-- generated: {fn:current-dateTime()} -->
</version>
<version num="5.0.9">
<!-- generated: {fn:current-dateTime()} -->
</version>
<version num="5.0.10">
<!-- generated: {fn:current-dateTime()} -->
</version>
<version num="5.0.11">
<!-- generated: {fn:current-dateTime()} -->
</version>
</pkg>
</repo>

View File

@ -1,20 +1,31 @@
# expkg-zone58.image.thumbnailator
An XQuery interface to the thumbnail generator library
[thumbnailator](https://github.com/coobird/thumbnailator).
# expkg-zone58:image.thumbnailator
An XQuery interface to the image thumbnail generator library
[thumbnailator](https://github.com/coobird/thumbnailator) for BaseX 8.6.2+.
Tested against BaseX 8.6.2
Features size, scale, caption, rotate, flip, colorize, watermark. More details at [doc](doc/readme.md)
## Usage
Images are input and output as `xs:base64Binary` items. Inputs typically come from `fetch:binary`
which allows for file and http sources. Outputs may be saved with `file:write-binary` or `db:store`
### Simple
Create a thumbnail of given size
```xquery
import module namespace t="expkg-zone58:image.thumbnailator";
fetch:binary("http://images.metmuseum.org/CRDImages/ep/original/DT46.jpg")
=>t:size(80,60)
=>t:size(80)
```
or scale to a fraction of the original
```xquery
import module namespace t="expkg-zone58:image.thumbnailator";
fetch:binary("file:///Z:/recordings/radio/Book%20of%20the%20Week/image.png")
=>t:scale(0.25)
```
### Tasks
@ -39,20 +50,19 @@ let $task:=
return fetch:binary("http://images.metmuseum.org/CRDImages/ep/original/DT46.jpg")
=>t:task($task)
```
A schema for this is [provided](./src/main/content/task.xsd)
The schema for this XML is available at [task.xsd](./src/main/content/task.xsd)
## Installation
The library is packaged in the [EXpath](http://expath.org/spec/pkg) xar format with
the thumbnailator jar included. See releases for installation instructions.
the thumbnailator jar included. See [releases](../releases) for installation instructions.
# Tests
`test.xqm` script uses the BaseX [Unit module](http://docs.basex.org/wiki/Unit_Module)
## License
* ex-thumbnailator Copyright (c) 2017, Andy Bunce. (Apache 2 License).
* ex-thumbnailator Copyright (c) 2016-2017, Andy Bunce. (Apache 2 License).
* thumbnailator Copyright (c) Chris Kroells (MIT License).
# todo
scale sourceregion

View File

@ -31,10 +31,10 @@ import net.coobird.thumbnailator.builders.ThumbnailParameterBuilder;
import net.coobird.thumbnailator.filters.Pipeline;
import net.coobird.thumbnailator.filters.Rotation;
import net.coobird.thumbnailator.filters.Watermark;
import net.coobird.thumbnailator.geometry.AbsoluteSize;
import net.coobird.thumbnailator.geometry.Position;
import net.coobird.thumbnailator.geometry.Positions;
import net.coobird.thumbnailator.geometry.Region;
import net.coobird.thumbnailator.geometry.Size;
import net.coobird.thumbnailator.tasks.StreamThumbnailTask;
import net.coobird.thumbnailator.filters.Canvas;
import net.coobird.thumbnailator.filters.Caption;
@ -51,18 +51,18 @@ import net.coobird.thumbnailator.filters.ImageFilter;
*/
public class Thumbs extends QueryModule{
public static B64Stream size(final B64Stream inputStream, final int width, final int height)
public B64Stream size(final B64Stream inputStream, final int width, final int height)
throws IOException, QueryException {
ByteArrayInputStream is = new ByteArrayInputStream(inputStream.binary(null));
ByteArrayOutputStream os = new ByteArrayOutputStream();
ThumbnailParameterBuilder builder = new ThumbnailParameterBuilder();
builder.size(new Dimension(width, height));
builder.size(width, height);
StreamThumbnailTask task = new StreamThumbnailTask(builder.build(), is, os);
Thumbnailator.createThumbnail(task);
return new B64Stream(new IOContent(os.toByteArray()), IOERR_X);
}
public static B64Stream scale(final B64Stream inputStream,
public B64Stream scale(final B64Stream inputStream,
final double xscale, final double yscale)
throws IOException, QueryException {
ByteArrayInputStream is = new ByteArrayInputStream(inputStream.binary(null));
@ -74,7 +74,7 @@ public class Thumbs extends QueryModule{
return new B64Stream(new IOContent(os.toByteArray()), IOERR_X);
}
public static B64Stream task(final B64Stream inputStream, final ANode thumbnail)
public B64Stream task(final B64Stream inputStream, final ANode thumbnail)
throws IOException, QueryException {
ByteArrayInputStream is = new ByteArrayInputStream(inputStream.binary(null));
ByteArrayOutputStream os = new ByteArrayOutputStream();
@ -85,13 +85,15 @@ public class Thumbs extends QueryModule{
}
// build parameters from XML
static ThumbnailParameter fromNode(final ANode node) throws QueryException, IOException {
ThumbnailParameter fromNode(final ANode node) throws QueryException, IOException {
ThumbnailParameterBuilder builder = new ThumbnailParameterBuilder();
Iterator<ANode> itr = node.children().iterator();
while (itr.hasNext()) {
ANode element = itr.next();
if (element.kind() == Data.ELEM) {
String name = Token.string(element.name());
// FnTrace.trace(name.getBytes(), "element: ".getBytes(), queryContext);
switch (name) {
case "size":
@ -106,8 +108,8 @@ public class Thumbs extends QueryModule{
region(builder, element);
break;
case "exif-orientation":
exif(builder, element);
case "constrain":
constrain(builder, element);
break;
case "filters":
@ -115,6 +117,12 @@ public class Thumbs extends QueryModule{
builder.filters(filters);
break;
case "output":
String format = Utils.attrib(element, "format",
ThumbnailParameter.ORIGINAL_FORMAT);
builder.format(format);
break;
default:
break;
}
@ -123,37 +131,45 @@ public class Thumbs extends QueryModule{
return builder.build();
}
static void region(final ThumbnailParameterBuilder builder, final ANode node)
void region(final ThumbnailParameterBuilder builder, final ANode node)
throws QueryException {
int width = (int) Int.parse(node.attribute("width"), null);
int height = (int) Int.parse(node.attribute("height"), null);
Dimension d = new Dimension(width, height);
Position pos = Utils.position(node, "position", Positions.CENTER);
Region r = new Region(pos, (Size) d);
Region r = new Region(pos, new AbsoluteSize(d));
builder.region(r);
}
static void exif(final ThumbnailParameterBuilder builder, final ANode node)
void constrain(final ThumbnailParameterBuilder builder, final ANode node)
throws QueryException {
boolean use = Utils.attrib(node, "use", true);
builder.useExifOrientation(use);
boolean aspect = Utils.attrib(node, "aspect", true);
builder.keepAspectRatio(aspect);
// FnTrace.trace(Boolean.toString(aspect).getBytes(), "constrain: ".getBytes(), queryContext);
boolean exif = Utils.attrib(node, "exif", true);
builder.useExifOrientation(exif);
boolean fit = Utils.attrib(node, "fit", true);
builder.fitWithinDimensions(fit);
}
static void size(final ThumbnailParameterBuilder builder, final ANode node)
void size(final ThumbnailParameterBuilder builder, final ANode node)
throws QueryException {
int width = (int) Int.parse(node.attribute("width"), null);
int height = (int) Int.parse(node.attribute("height"), null);
builder.size(width, height);
}
static void scale(final ThumbnailParameterBuilder builder, final ANode node)
void scale(final ThumbnailParameterBuilder builder, final ANode node)
throws QueryException {
double x = Utils.attrib(node, "x", 0.5f);
double y = Utils.attrib(node, "y", 0.5f);
builder.scale(x, y);
}
static List<ImageFilter> filters(final ANode filters) throws QueryException, IOException {
List<ImageFilter> filters(final ANode filters) throws QueryException, IOException {
Pipeline pipeline = new Pipeline();
Iterator<ANode> itr = filters.children().iterator();
while (itr.hasNext()) {
@ -193,7 +209,7 @@ public class Thumbs extends QueryModule{
return pipeline.getFilters();
}
private static void watermark(final Pipeline pipeline, final ANode node)
private void watermark(final Pipeline pipeline, final ANode node)
throws IOException, QueryException {
ImageFilter filter;
Position pos;
@ -204,13 +220,13 @@ public class Thumbs extends QueryModule{
pipeline.add(filter);
}
private static void rotate(final Pipeline pipeline, final ANode node)
private void rotate(final Pipeline pipeline, final ANode node)
throws QueryException {
double angle = (double) Dbl.parse(node.attribute("angle"), null);
pipeline.add(Rotation.newRotator(angle));
}
private static void flip(final Pipeline pipeline, final ANode node) {
private void flip(final Pipeline pipeline, final ANode node) {
ImageFilter filter;
String axis = Token.string(node.attribute("axis"));
// FnTrace.trace(axis.getBytes(), "FLIP: ".getBytes(), queryContext);
@ -218,7 +234,7 @@ public class Thumbs extends QueryModule{
pipeline.add(filter);
}
private static void colorize(final Pipeline pipeline, final ANode node) throws QueryException {
private void colorize(final Pipeline pipeline, final ANode node) throws QueryException {
ImageFilter filter;
String color;
color = Utils.attrib(node, "color", "black");
@ -227,7 +243,7 @@ public class Thumbs extends QueryModule{
pipeline.add(filter);
}
private static void canvas(final Pipeline pipeline, final ANode node) throws QueryException {
private void canvas(final Pipeline pipeline, final ANode node) throws QueryException {
ImageFilter filter;
String color;
Position pos;
@ -239,7 +255,7 @@ public class Thumbs extends QueryModule{
pipeline.add(filter);
}
private static void caption(final Pipeline pipeline, final ANode node) throws QueryException {
private void caption(final Pipeline pipeline, final ANode node) throws QueryException {
ImageFilter filter;
String color;
Position pos;
@ -248,9 +264,10 @@ public class Thumbs extends QueryModule{
pos = Utils.position(node, "position", Positions.TOP_CENTER);
String fontName = Utils.attrib(node, "font", "SansSerif");
int size = Utils.attrib(node, "size", 14);
int insets = Utils.attrib(node, "insets", 0);
Font font = new Font(fontName, Font.PLAIN, size);
filter = new Caption(text, font , Utils.stringToColor(color),
pos, 0);
pos, insets);
pipeline.add(filter);
}
}

View File

@ -1,5 +1,5 @@
<package xmlns="http://www.basex.org/modules/pkg">
<jar>thumbhelper-0.4.17.jar</jar>
<jar>thumbhelper-5.0.11.jar</jar>
<jar>thumbnailator-0.4.8.jar</jar>
<class>org.expkgzone58.image.Thumbs</class>
</package>

View File

@ -2,6 +2,9 @@
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- thumbnail schema apb apr 2017 -->
<xs:element name="thumbnail">
<xs:annotation>
<xs:documentation>Root container</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:choice>
@ -9,42 +12,54 @@
<xs:element ref="scale" />
</xs:choice>
<xs:element ref="region" minOccurs="0" />
<xs:element ref="exif-orientation" minOccurs="0" />
<xs:element ref="constrain" minOccurs="0" />
<xs:element ref="filters" minOccurs="0" />
<xs:element ref="output" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="size">
<xs:annotation>
<xs:documentation>size of thumbnail to create</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attribute name="width" type="xs:int" />
<xs:attribute name="height" type="xs:int" />
<xs:attribute name="width" type="PxType" />
<xs:attribute name="height" type="PxType" />
</xs:complexType>
</xs:element>
<xs:element name="scale">
<xs:annotation>
<xs:documentation>scale thumbnail from source size</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attribute name="x" type="xs:double" />
<xs:attribute name="y" type="xs:double" />
<xs:attribute name="x" type="ScaleType" />
<xs:attribute name="y" type="ScaleType" />
</xs:complexType>
</xs:element>
<xs:element name="region">
<xs:annotation>
<xs:documentation>region of source image to use for thumbnail.
default all
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attribute name="height" type="xs:int" />
<xs:attribute name="width" type="xs:int" />
<xs:attribute name="height" type="PxType" />
<xs:attribute name="width" type="PxType" />
<xs:attribute name="position" type="PositionType" />
</xs:complexType>
</xs:element>
<xs:element name="exif-orientation">
<xs:complexType>
<xs:attribute name="use" type="xs:boolean" />
</xs:complexType>
</xs:element>
<xs:element name="filters">
<xs:annotation>
<xs:documentation>contain for filters to be applied to thumbnail
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:choice maxOccurs="unbounded">
<xs:element ref="canvas" minOccurs="0" />
@ -58,56 +73,84 @@
</xs:element>
<xs:element name="canvas">
<xs:annotation>
<xs:documentation>create a filled enclosing background for thumbnail
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attribute name="height" type="xs:int" />
<xs:attribute name="width" type="xs:int" />
<xs:attribute name="color" type="xs:string" />
<xs:attribute name="height" type="PxType" />
<xs:attribute name="width" type="PxType" />
<xs:attribute name="color" type="ColorType" />
<xs:attribute name="position" type="PositionType" />
</xs:complexType>
</xs:element>
<xs:element name="colorize">
<xs:annotation>
<xs:documentation>tint thumbnail</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attribute name="color" type="xs:string" />
<xs:attribute name="alpha" type="xs:float" />
<xs:attribute name="color" type="ColorType" />
<xs:attribute name="alpha" type="AlphaType" />
</xs:complexType>
</xs:element>
<xs:element name="caption">
<xs:annotation>
<xs:documentation>add text to thumbnail</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="size" type="xs:int" />
<xs:attribute name="font" type="xs:string" />
<xs:attribute name="color" type="xs:string" />
<xs:attribute name="color" type="ColorType" />
<xs:attribute name="position" type="PositionType" />
<xs:attribute name="insets"
type="xs:int">
</xs:attribute>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name="flip">
<xs:annotation>
<xs:documentation>flip thumbnail horizontally or vertically.
**Ignored if exif-orientation is true **
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attribute name="axis" type="AxisType" />
</xs:complexType>
</xs:element>
<xs:element name="rotate">
<xs:annotation>
<xs:documentation>rotate the thumbnail</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attribute name="angle" type="xs:float" />
<xs:attribute name="angle" type="xs:float" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="watermark">
<xs:annotation>
<xs:documentation>Add image to thumbnail as a watermark
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attribute name="src" type="xs:string" />
<xs:attribute name="position" type="PositionType" />
<xs:attribute name="alpha" type="xs:float" />
<xs:attribute name="alpha" type="AlphaType" />
</xs:complexType>
</xs:element>
<xs:simpleType name="AxisType">
<xs:annotation>
<xs:documentation>Axis of flip</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string">
<xs:enumeration value="horizontal" />
<xs:enumeration value="vertical" />
@ -115,6 +158,9 @@
</xs:simpleType>
<xs:simpleType name="PositionType">
<xs:annotation>
<xs:documentation>Region of image to use</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string">
<xs:enumeration value="TOP_LEFT" />
<xs:enumeration value="TOP_CENTER" />
@ -130,4 +176,87 @@
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="PxType">
<xs:annotation>
<xs:documentation>A size in pixels</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:int">
<xs:minInclusive value="0"></xs:minInclusive>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="ScaleType">
<xs:restriction base="xs:double">
<xs:minExclusive value="0"></xs:minExclusive>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="AlphaType">
<xs:annotation>
<xs:documentation>Transparency alpha value</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:double">
<xs:minInclusive value="0"></xs:minInclusive>
<xs:maxInclusive value="1"></xs:maxInclusive>
</xs:restriction>
</xs:simpleType>
<xs:element name="constrain">
<xs:annotation>
<xs:documentation>constraints on thumbnail</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attribute name="exif" type="xs:boolean" default="true">
<xs:annotation>
<xs:documentation> true if the Exif metadata should be used to
determine the orientation of the thumbnail,
false otherwise.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="fit" type="xs:boolean" use="optional"
default="true">
<xs:annotation>
<xs:documentation>fit - true if the thumbnail should be sized to
fit within the specified dimensions,
if the thumbnail is going to
exceed those dimensions.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="aspect" type="xs:boolean" use="optional"
default="true">
<xs:annotation>
<xs:documentation>keep aspect ratio</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
<xs:simpleType name="ColorType">
<xs:annotation>
<xs:documentation>Java colour name e.g. "red"
or Color.decode(value)
decimal, octal, or hexidecimal integer e.g
"#FF0096"
</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string"></xs:restriction>
</xs:simpleType>
<xs:element name="output" >
<xs:annotation>
<xs:documentation>Serialization details for output</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attribute name="format" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>Output image format (JPEG, PNG, GIF, BMP and
WBMP). defaults to source format</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

Binary file not shown.

View File

@ -2,48 +2,83 @@ xquery version "3.1" encoding "UTF-8";
(:~ Generate image thumbnails using the thumbnailator library.
: @see https://github.com/coobird/thumbnailator
: @author andy bunce
: @version 4.4.1
: @version 0.5
:)
module namespace thumbnails = 'expkg-zone58:image.thumbnailator';
import module namespace Thumbs = "org.expkgzone58.image.Thumbs";
(:~
: generate scaled version of source image with maximum dimension of size
: @param $source base64Binary (streamed?) e.g from `fetch:binary`
: @return base64Binary for thumbnail
:)
declare function thumbnails:size($source as xs:base64Binary,$size as xs:integer)
as xs:base64Binary
{
Thumbs:size($source,xs:int($size),xs:int($size))
};
(:~
: generate scaled version of source image with maximum dimension of size
: @param $source base64Binary (streamed?) e.g from `fetch:binary`
: @result base64Binary for thumbnail
: @return base64Binary for thumbnail
:)
declare function thumbnails:size($source as xs:base64Binary,$width as xs:integer,$height as xs:integer)
as xs:base64Binary
{
Q{java:org.expkgzone58.image.Thumbs}size($source,xs:int($width),xs:int($height))
Thumbs:size($source,xs:int($width),xs:int($height))
};
(:~
: generate scaled version of source image at given factors 0-1
: @param $source base64Binary (streamed?) e.g from `fetch:binary`
: @result base64Binary for thumbnail
: @return the thumbnail
:)
declare function thumbnails:scale($source as xs:base64Binary,$xscale as xs:float,$yscale as xs:float)
declare function thumbnails:scale($source as xs:base64Binary,$scale as xs:double)
as xs:base64Binary
{
Q{java:org.expkgzone58.image.Thumbs}scale($source,$xscale,$yscale)
Thumbs:scale($source,$scale,$scale)
};
(:~
: generate scaled version of source image at given factors 0-1
: @param $source base64Binary (streamed?) e.g from `fetch:binary`
: @return the thumbnail
:)
declare function thumbnails:scale($source as xs:base64Binary,$xscale as xs:double,$yscale as xs:double)
as xs:base64Binary
{
Thumbs:scale($source,$xscale,$yscale)
};
(:~
: generate thumbnail using parameters specified via XML
: @param $source base64Binary (streamed?) e.g from `fetch:binary`
: @param $task XML parameters <task><size width="100" ..
: @result base64Binary for thumbnail
: @return the thumbnail
:)
declare function thumbnails:task($source as xs:base64Binary,
$task as element(thumbnail))
as xs:base64Binary
{
Q{java:org.expkgzone58.image.Thumbs}task($source,$task)
Thumbs:task($source,$task)
};
(:~
: validate task XML against schema
: @param $src XML parameters <task><size width="100" ..
: @result validation report
: validate task thumbnail XML against schema
: @param $src XML parameters <thumbnail><size width="100" ..
: @return empty-sequence or error
: @error BXVA0001: the validation fails.
: @error BXVA0002: the validation process cannot be started.
: @error BXVA0003: no XML Schema validator is available.
: @error BXVA0004: no validator is found for the specified version.
:)
declare function thumbnails:validate($src)
as empty-sequence()
{
validate:xsd($src,"task.xsd")
};
(:~
: validate task thumbnail XML against schema
: @param $src XML parameters <thumbnail><size width="100" ..
: @return validation report
:)
declare function thumbnails:validation-report($src)
as element(report)

View File

@ -1,5 +1,5 @@
<package xmlns="http://expath.org/ns/pkg" name="https://github.com/expkg-zone58/ex-thumbnailator"
abbrev="thumbnailator" version="4.1.5" spec="1.0">
abbrev="thumbnailator" version="5.0.11" spec="1.0">
<title>An XQuery interface to thumbnailator the thumbnail generator library.</title>
<dependency processor="basex" />

34
src/test/copy.xq Normal file
View File

@ -0,0 +1,34 @@
(:~
:examples of constrain use
:)
import module namespace t="expkg-zone58:image.thumbnailator";
declare variable $file-base:=file:parent(static-base-uri());
declare variable $watermark:="C:\Users\andy\git\ex-thumbnailator\src\test\resources\icon.gif";
declare variable $src:=file:resolve-path("resources/A34283.jpg",$file-base);
declare function local:wi($data as xs:base64Binary,$filename as xs:string)
{
file:write-binary(file:resolve-path($filename,$file-base),$data)
};
declare function local:constrain($aspect as xs:boolean,$fit as xs:boolean,$exif as xs:boolean)
{
<constrain aspect="{$aspect}" fit="{$fit}" exif="{$exif}"/>
};
let $s:=function($b){if($b) then ".+" else ".-"}
let $img:= fetch:binary($src)
let $ft:=(false(),true())
for $fit in $ft,$aspect in $ft,$exif in $ft
let $file:=("out",
$s($aspect) , "aspect",
$s($fit), "fit",
$s($exif), "exif")=>string-join("")
let $task:=<thumbnail>
<size width="250" height="250"/>
{local:constrain($aspect,$fit,$exif)}
<filters>
<canvas height="300" width="300" color="lightGray" position="CENTER"/>
<caption size="20" color="black" position="TOP_CENTER">{$file}</caption>
<watermark src="{$watermark}" alpha="1" position="BOTTOM_RIGHT"/>
</filters>
<output format="gif"/>
</thumbnail>
return t:task($img,$task) => local:wi("resources/" || $file || ".gif")

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

BIN
src/test/resources/icon.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -1,6 +1,7 @@
<thumbnail xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="expkg-zone58:image.thumbnailator/schema/task.xsd">
<size width="200" height="200" />
<constrain fit="true" aspect="true" exif="true"/>
<filters>
<rotate angle="251" />
<rotate angle="1" />

View File

@ -12,11 +12,11 @@ let $task:=<thumbnail>
<rotate angle="15"/>
<canvas height="500" width="500" position="TOP_LEFT" color="black"/>
<watermark src="{$watermark}" alpha=".8" position="TOP_LEFT"/>
<watermark src="{$watermark}" alpha=".8" position="BOTTOM_LEFT"/>
</filters>
</thumbnail>
(: let $task:=doc("rotate.xml")/task :)
let $x:=fetch:binary($remote)
let $r:=Q{java:org.expkgzone58.image.Thumbs}size($x,xs:int(40),xs:int(40))
let $r:=t:task($x,$task)
(: let $r:=Q{java:org.expkgzone58.image.Thumbs}task($x,$task) :)
return file:write-binary($picr,$r)