Commit 493ae41f authored by C. Boemann's avatar C. Boemann

Load and save the contour-polygon and contour-path elements.

They are stored as KoClipPath in the engine. Previously the engine only
had support for loading and saving KoClipPath in svg

Load and save the style frame style attributes that turn contoured text run around
(wrapping) on and off. wrap-contour and wrap-contour-mode

Use this new technology to do tight run around (simple change only requesting what we already had)

This commit also adds a method to pictureshape to create a tightfitting outline path of the image.
It's tested and works, but is not used yet to create a KoClipPath out of it.

REVIEW: 104873
parent 0dab13a4
......@@ -111,6 +111,57 @@ KoPathShape::~KoPathShape()
clear();
}
void KoPathShape::saveContourOdf(KoShapeSavingContext &context, const QSizeF &scaleFactor) const
{
Q_D(const KoPathShape);
if (m_subpaths.length() <= 1) {
QTransform matrix;
matrix.scale(scaleFactor.width(), scaleFactor.height());
QString points;
KoSubpath *subPath = m_subpaths.first();
KoSubpath::const_iterator pointIt(subPath->constBegin());
KoPathPoint *currPoint= 0;
// iterate over all points
for (; pointIt != subPath->constEnd(); ++pointIt) {
currPoint = *pointIt;
if (currPoint->activeControlPoint1() || currPoint->activeControlPoint2()) {
break;
}
const QPointF p = matrix.map(currPoint->point());
points += QString("%1,%2 ").arg(qRound(1000*p.x())).arg(qRound(1000*p.y()));
}
if (currPoint && !(currPoint->activeControlPoint1() || currPoint->activeControlPoint2())) {
context.xmlWriter().startElement("draw:contour-polygon");
context.xmlWriter().addAttributePt("svg:width", size().width());
context.xmlWriter().addAttributePt("svg:height", size().height());
const QSizeF s(size());
QString viewBox = QString("0 0 %1 %2").arg(qRound(1000*s.width())).arg(qRound(1000*s.height()));
context.xmlWriter().addAttribute("svg:viewBox", viewBox);
context.xmlWriter().addAttribute("draw:points", points);
context.xmlWriter().addAttribute("draw:recreate-on-edit", "true");
context.xmlWriter().endElement();
return;
}
}
// if we get here we couldn't save as polygon - let-s try contour-path
context.xmlWriter().startElement("draw:contour-path");
saveOdfAttributes(context, OdfViewbox);
context.xmlWriter().addAttribute("svg:d", toString());
context.xmlWriter().addAttribute("calligra:nodeTypes", d->nodeTypes());
context.xmlWriter().addAttribute("draw:recreate-on-edit", "true");
context.xmlWriter().endElement();
}
void KoPathShape::saveOdf(KoShapeSavingContext & context) const
{
Q_D(const KoPathShape);
......@@ -125,6 +176,59 @@ void KoPathShape::saveOdf(KoShapeSavingContext & context) const
context.xmlWriter().endElement();
}
bool KoPathShape::loadContourOdf(const KoXmlElement & element, KoShapeLoadingContext &context, const QSizeF &scaleFactor)
{
Q_D(KoPathShape);
// first clear the path data from the default path
clear();
if (element.localName() == "contour-polygon") {
QString points = element.attributeNS(KoXmlNS::draw, "points").simplified();
points.replace(',', ' ');
points.remove('\r');
points.remove('\n');
bool firstPoint = true;
const QStringList coordinateList = points.split(' ');
for (QStringList::ConstIterator it = coordinateList.constBegin(); it != coordinateList.constEnd(); ++it) {
QPointF point;
point.setX((*it).toDouble());
++it;
point.setY((*it).toDouble());
if (firstPoint) {
moveTo(point);
firstPoint = false;
} else
lineTo(point);
}
close();
} else if (element.localName() == "contour-path") {
KoPathShapeLoader loader(this);
loader.parseSvg(element.attributeNS(KoXmlNS::svg, "d"), true);
d->loadNodeTypes(element);
}
// apply viewbox transformation
QRectF viewBox = KoPathShape::loadOdfViewbox(element);
if (! viewBox.isEmpty()) {
QSizeF size;
size.setWidth(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "width", QString())));
size.setHeight(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "height", QString())));
// create matrix to transform original path data into desired size and position
QTransform viewMatrix;
viewMatrix.translate(-viewBox.left(), -viewBox.top());
viewMatrix.scale(scaleFactor.width(), scaleFactor.height());
viewMatrix.scale(size.width() / viewBox.width(), size.height() / viewBox.height());
// transform the path data
d->map(viewMatrix);
}
setTransformation(QTransform());
return true;
}
bool KoPathShape::loadOdf(const KoXmlElement & element, KoShapeLoadingContext &context)
{
Q_D(KoPathShape);
......
......@@ -117,6 +117,15 @@ public:
// reimplemented
virtual bool loadOdf(const KoXmlElement &element, KoShapeLoadingContext &context);
// basically the same as loadOdf but adapted to the contour cases
// tag needs to be either contour-polygon or contour-path from another shape
bool loadContourOdf(const KoXmlElement & element, KoShapeLoadingContext &context, const QSizeF &scaleFactor);
/** basically the equivalent saveOdf but adapted to the contour cases
* @param originalSize the original size of the unscaled image.
*/
void saveContourOdf(KoShapeSavingContext &context, const QSizeF &originalSize) const;
/// Removes all subpaths and their points from the path
void clear();
/**
......
......@@ -44,6 +44,7 @@
#include "ShapeDeleter_p.h"
#include "KoShapeShadow.h"
#include "KoClipPath.h"
#include "KoPathShape.h"
#include "KoEventAction.h"
#include "KoEventActionRegistry.h"
#include "KoOdfWorkaround.h"
......@@ -97,7 +98,8 @@ KoShapePrivate::KoShapePrivate(KoShape *shape)
protectContent(false),
textRunAroundSide(KoShape::BiggestRunAroundSide),
textRunAroundDistance(1.0),
textRunAroundThreshold(0.0)
textRunAroundThreshold(0.0),
textRunAroundContour(KoShape::ContourFull)
{
connectors[KoConnectionPoint::TopConnectionPoint] = KoConnectionPoint::defaultConnectionPoint(KoConnectionPoint::TopConnectionPoint);
connectors[KoConnectionPoint::RightConnectionPoint] = KoConnectionPoint::defaultConnectionPoint(KoConnectionPoint::RightConnectionPoint);
......@@ -938,6 +940,18 @@ void KoShape::setTextRunAroundThreshold(qreal threshold)
d->textRunAroundThreshold = threshold;
}
KoShape::TextRunAroundContour KoShape::textRunAroundContour() const
{
Q_D(const KoShape);
return d->textRunAroundContour;
}
void KoShape::setTextRunAroundContour(KoShape::TextRunAroundContour contour)
{
Q_D(KoShape);
d->textRunAroundContour = contour;
}
void KoShape::setBackground(KoShapeBackground *fill)
{
Q_D(KoShape);
......@@ -950,7 +964,7 @@ void KoShape::setBackground(KoShapeBackground *fill)
notifyChanged();
}
KoShapeBackground * KoShape::background() const
KoShapeBackground *KoShape::background() const
{
Q_D(const KoShape);
return d->fill;
......@@ -1281,6 +1295,19 @@ QString KoShape::saveStyle(KoGenStyle &style, KoShapeSavingContext &context) con
break;
}
style.addProperty("style:wrap", wrap, KoGenStyle::GraphicType);
switch (textRunAroundContour()) {
case ContourBox:
style.addProperty("style:wrap-contour", "false", KoGenStyle::GraphicType);
break;
case ContourFull:
style.addProperty("style:wrap-contour", "true", KoGenStyle::GraphicType);
style.addProperty("style:wrap-contour-mode", "full", KoGenStyle::GraphicType);
break;
case ContourOutside:
style.addProperty("style:wrap-contour", "true", KoGenStyle::GraphicType);
style.addProperty("style:wrap-contour-mode", "outside", KoGenStyle::GraphicType);
break;
}
style.addPropertyPt("style:wrap-dynamic-threshold", textRunAroundThreshold(), KoGenStyle::GraphicType);
style.addPropertyPt("fo:margin", textRunAroundDistance(), KoGenStyle::GraphicType);
......@@ -1360,6 +1387,15 @@ void KoShape::loadStyle(const KoXmlElement &element, KoShapeLoadingContext &cont
setTextRunAroundThreshold(KoUnit::parseValue(wrapThreshold));
}
}
if (styleStack.property(KoXmlNS::style, "wrap-contour", "false") == "true") {
if (styleStack.property(KoXmlNS::style, "wrap-contour-mode", "full") == "full") {
setTextRunAroundContour(KoShape::ContourFull);
} else {
setTextRunAroundContour(KoShape::ContourOutside);
}
} else {
setTextRunAroundContour(KoShape::ContourBox);
}
}
bool KoShape::loadOdfAttributes(const KoXmlElement &element, KoShapeLoadingContext &context, int attributes)
......@@ -1665,6 +1701,28 @@ void KoShape::loadOdfGluePoints(const KoXmlElement &element, KoShapeLoadingConte
kDebug(30006) << "shape has now" << d->connectors.count() << "glue-points";
}
void KoShape::loadOdfClipContour(const KoXmlElement &element, KoShapeLoadingContext &context, const QSizeF &scaleFactor)
{
Q_D(KoShape);
KoXmlElement child;
forEachElement(child, element) {
if (child.namespaceURI() != KoXmlNS::draw)
continue;
if (child.localName() != "contour-polygon")
continue;
kDebug(30006) << "shape loads contour-polygon";
KoPathShape *ps = new KoPathShape();
ps->loadContourOdf(child, context, scaleFactor);
ps->setTransformation(transformation());
KoClipData *cd = new KoClipData(ps);
KoClipPath *clipPath = new KoClipPath(this, cd);
d->clipPath = clipPath;
}
}
QTransform KoShape::parseOdfTransform(const QString &transform)
{
QTransform matrix;
......@@ -1947,6 +2005,19 @@ void KoShape::saveOdfCommonChildElements(KoShapeSavingContext &context) const
}
}
void KoShape::saveOdfClipContour(KoShapeSavingContext &context, const QSizeF &originalSize) const
{
Q_D(const KoShape);
kDebug(30006) << "shape saves contour-polygon";
if (d->clipPath && !d->clipPath->clipPathShapes().isEmpty()) {
// This will loose data as odf can only save one set of contour wheras
// svg loading and at least karbon editing can produce more than one
// TODO, FIXME see if we can save more than one clipshape to odf
d->clipPath->clipPathShapes().first()->saveContourOdf(context, originalSize);
}
}
// end loading & saving methods
// static
......
......@@ -144,6 +144,13 @@ public:
RunThrough ///< The text will completely ignore the frame and layout as if it was not there
};
/// The behavior text should do when intersecting this shape.
enum TextRunAroundContour {
ContourBox, /// Run other text around a bounding rect of the outline
ContourFull, ///< Run other text around also on the inside
ContourOutside ///< Run other text around only on the outside
};
/**
* TODO
*/
......@@ -212,9 +219,19 @@ public:
* This method can be used while saving the shape as Odf to add common child elements
*
* The office:event-listeners and draw:glue-point are saved.
* @param context the context for the current save.
*/
void saveOdfCommonChildElements(KoShapeSavingContext &context) const;
/**
* This method can be used to save contour data from the clipPath()
*
* The draw:contour-polygon or draw:contour-path elements are saved.
* @param context the context for the current save.
* @param originalSize the original size of the unscaled image.
*/
void saveOdfClipContour(KoShapeSavingContext &context, const QSizeF &originalSize) const;
/**
* @brief Scale the shape using the zero-point which is the top-left corner.
* @see position()
......@@ -400,6 +417,18 @@ public:
*/
void setTextRunAroundThreshold(qreal threshold);
/**
* Return the how tight text run around is done around this shape.
* @return the contour
*/
TextRunAroundContour textRunAroundContour() const;
/**
* Set how tight text run around is done around this shape.
* @param contour the new contour
*/
void setTextRunAroundContour(TextRunAroundContour contour);
/**
* Set the background of the shape.
* A shape background can be a plain color, a gradient, a pattern, be fully transparent
......@@ -1092,6 +1121,9 @@ protected:
/// Loads the connection points
void loadOdfGluePoints(const KoXmlElement &element, KoShapeLoadingContext &context);
/// Loads the clip contour
void loadOdfClipContour(const KoXmlElement &element, KoShapeLoadingContext &context, const QSizeF &scaleFactor);
/* ** end loading saving */
/**
......
......@@ -92,10 +92,9 @@ public:
int protectContent : 1;
KoShape::TextRunAroundSide textRunAroundSide;
qreal textRunAroundDistance;
qreal textRunAroundThreshold;
KoShape::TextRunAroundContour textRunAroundContour;
/// Convert connection point position from shape coordinates, taking alignment into account
void convertFromShapeCoordinates(KoConnectionPoint &point, const QSizeF &shapeSize) const;
......
......@@ -23,6 +23,7 @@
#include <KoShapeStrokeModel.h>
#include <KoShapeShadow.h>
#include <KoShapeGroup.h>
#include <KoClipPath.h>
#include <qnumeric.h>
......@@ -72,7 +73,17 @@ QPainterPath KoTextLayoutObstruction::decoratedOutline(const KoShape *shape, qre
return groupPath;
}
QPainterPath path = shape->outline();
QPainterPath path;
if (shape->textRunAroundContour() != KoShape::ContourBox) {
KoClipPath *clipPath = shape->clipPath();
if (clipPath) {
path = clipPath->pathForSize(shape->size());
} else {
path = shape->outline();
}
} else {
path.addRect(shape->outlineRect());
}
QRectF bb = shape->outlineRect();
borderHalfWidth = 0;
......
......@@ -4,6 +4,7 @@
* Copyright (C) 2008 Thorsten Zachmann <zachmann@kde.org>
* Copyright (C) 2011 Silvio Heinrich <plassy@web.de>
* Copyright (C) 2012 Inge Wallin <inge@lysator.liu.se>
* Copyright (C) 2012 C.Boemann <cbo@boemann.dk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
......@@ -38,6 +39,7 @@
#include <KoUnit.h>
#include <KoGenStyle.h>
#include <KoFilterEffectStack.h>
#include <KoClipPath.h>
#include <SvgSavingContext.h>
#include <SvgLoadingContext.h>
#include <SvgUtil.h>
......@@ -50,6 +52,8 @@
#include <QTimer>
#include <QPixmapCache>
#include <QThreadPool>
#include <QImage>
#include <QColor>
QString generate_key(qint64 key, const QSize & size)
{
......@@ -58,7 +62,7 @@ QString generate_key(qint64 key, const QSize & size)
// ----------------------------------------------------------------- //
_Private::PixmapScaler::PixmapScaler(PictureShape* pictureShape, const QSize& pixmapSize):
_Private::PixmapScaler::PixmapScaler(PictureShape *pictureShape, const QSize &pixmapSize):
m_size(pixmapSize)
{
m_image = pictureShape->imageData()->image();
......@@ -82,7 +86,7 @@ void _Private::PixmapScaler::run()
// ----------------------------------------------------------------- //
void _Private::PictureShapeProxy::setImage(const QString& key, const QImage& image)
void _Private::PictureShapeProxy::setImage(const QString &key, const QImage &image)
{
QPixmapCache::insert(key, QPixmap::fromImage(image));
m_pictureShape->update();
......@@ -90,6 +94,65 @@ void _Private::PictureShapeProxy::setImage(const QString& key, const QImage& ima
// ----------------------------------------------------------------- //
QPainterPath _Private::generateOutline(const QImage &imageIn, int treshold)
{
int leftArray[100];
int rightArray[100];
QImage image = imageIn.scaled(QSize(100, 100));
QPainterPath path;
for (int y = 0; y < 100; y++) {
leftArray[y] = -1;
for (int x = 0; x < 100; x++) {
int a = qAlpha(image.pixel(x,y));
if (a > treshold) {
leftArray[y] = x;
break;
}
}
}
for (int y = 0; y < 100; y++) {
rightArray[y] = -1;
if (leftArray[y] != -1) {
for (int x = 100-1; x >= 0; x--) {
int a = qAlpha(image.pixel(x,y));
if (a > treshold) {
rightArray[y] = x;
break;
}
}
}
}
// Now we know the outline let's make a path out of it
bool first = true;
for (int y = 0; y < 100; y++) {
if (rightArray[y] != -1) {
if (first) {
path.moveTo(rightArray[y] / 99.0, y / 99.0);
first = false;
} else {
path.lineTo(rightArray[y] / 99.0, y / 99.0);
}
}
}
if (first) {
// Completely empty
return path;
}
for (int y = 100-1; y >= first; y--) {
if (leftArray[y] != -1) {
path.lineTo(leftArray[y] / 99.0, y / 99.0);
}
}
return path;
}
// ----------------------------------------------------------------- //
PictureShape::PictureShape()
: KoFrameShape(KoXmlNS::draw, "image")
, m_imageCollection(0)
......@@ -318,6 +381,9 @@ void PictureShape::saveOdf(KoShapeSavingContext &context) const
writer.addAttribute("xlink:href", name);
saveText(context);
writer.endElement(); // draw:image
QSizeF scaleFactor(imageData->imageSize().width() / size().width(),
imageData->imageSize().height() / size().height());
saveOdfClipContour(context, scaleFactor);
writer.endElement(); // draw:frame
context.addDataCenter(m_imageCollection);
......@@ -326,7 +392,19 @@ void PictureShape::saveOdf(KoShapeSavingContext &context) const
bool PictureShape::loadOdf(const KoXmlElement &element, KoShapeLoadingContext &context)
{
loadOdfAttributes(element, context, OdfAllAttributes);
return loadOdfFrame(element, context);
if (loadOdfFrame(element, context)) {
// load contour (clip)
KoImageData *imageData = qobject_cast<KoImageData*>(userData());
QSizeF scaleFactor(size().width() / imageData->imageSize().width(),
size().height() / imageData->imageSize().height());
loadOdfClipContour(element, context, scaleFactor);
return true;
}
return false;
}
bool PictureShape::loadOdfFrameElement(const KoXmlElement &element, KoShapeLoadingContext &context)
......@@ -367,6 +445,10 @@ QString PictureShape::saveStyle(KoGenStyle& style, KoShapeSavingContext& context
style.addProperty("draw:image-opacity", QString("%1%").arg((1.0 - transparency()) * 100.0));
}
// this attribute is need to work around a bug in LO 3.4 to make it recognice us as an
// image and not just any shape. But we shouldn't produce illegal odf so: only for testing!
// style.addAttribute("style:parent-style-name", "dummy");
// Mirroring
if (m_mirrorMode != MirrorNone) {
QString mode;
......@@ -406,8 +488,8 @@ QString PictureShape::saveStyle(KoGenStyle& style, KoShapeSavingContext& context
KoImageData *imageData = qobject_cast<KoImageData*>(userData());
if (imageData != 0) {
QSizeF imageSize = imageData->imageSize();
ClippingRect rect = m_clippingRect;
QSizeF imageSize = imageData->imageSize();
ClippingRect rect = m_clippingRect;
rect.normalize(imageSize);
rect.bottom = 1.0 - rect.bottom;
......@@ -454,7 +536,7 @@ void PictureShape::loadStyle(const KoXmlElement& element, KoShapeLoadingContext&
if (mirrorMode.contains("vertical")) {
mode |= MirrorVertical;
}
m_mirrorMode = mode;
}
......
......@@ -70,17 +70,22 @@ namespace _Private
{
Q_OBJECT
public:
PixmapScaler(PictureShape *pictureShape, const QSize& pixmapSize);
PixmapScaler(PictureShape *pictureShape, const QSize &pixmapSize);
virtual void run();
signals:
void finished(const QString&, const QImage&);
void finished(const QString &, const QImage &);
private:
QSize m_size;
QImage m_image;
quint64 m_imageKey;
};
/**
* This method will create an outline path out of the image
*/
QPainterPath generateOutline(const QImage &imageIn, int treshold = 20);
}
......@@ -144,10 +149,10 @@ public:
protected:
virtual bool loadOdfFrameElement(const KoXmlElement &element, KoShapeLoadingContext &context);
virtual QString saveStyle(KoGenStyle &style, KoShapeSavingContext &context) const;
virtual void loadStyle(const KoXmlElement& element, KoShapeLoadingContext& context);
virtual void loadStyle(const KoXmlElement &element, KoShapeLoadingContext &context);
private:
QSize calcOptimalPixmapSize(const QSizeF& shapeSize, const QSizeF& imageSize) const;
QSize calcOptimalPixmapSize(const QSizeF &shapeSize, const QSizeF &imageSize) const;
ClippingRect parseClippingRectString(QString string) const;
private:
......@@ -156,8 +161,8 @@ private:
mutable QSizeF m_printQualityRequestedSize;
QFlags<MirrorMode> m_mirrorMode;
ColorMode m_colorMode;
ClippingRect m_clippingRect;
ColorMode m_colorMode;
ClippingRect m_clippingRect;
_Private::PictureShapeProxy m_proxy;
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment