Wrapping Text Inside Irregular Shapes

Contain Text Within Irregular Shape

You can use css perspective here. Just apply it to the parent, then apply the reverse effect to the child. This way older browsers will simply display a square background, + responsive won't be an issue!

.container {  font-family: Monospace;  border: 3px solid black;  text-align: center;  padding: 0 20px;  transform: perspective(10px) rotateX(-0.3deg);  border-radius: 8px;  max-width: 600px;}
.container div { transform: perspective(10px) rotateX(0.3deg);}
h2 { display: inline-block; padding: 0 10px; background-color: #fff; margin: -10px 0 0;}
<div class="container">  <div>    <h2>What's the difference? Craft vs plant</h2>    <p>I am trying to achieve the following in the most responsive, backward compatible way as possible. I appreciate that I may need to make a big compromise somewhere.</p>  </div></div>

How can I wrap text around a non rectangular image?

You can use this method, where you float divs to block off the shape's area.

So - the answer is "Yes - it can be done". But as far as I know there's no "easy" way like a CSS "text-wrap" option.

How to find where to draw text inside an irregular shape represented by a UIBezierPath?

TextKit was built for tasks like this. You can create an array of paths outside of your bezier shape path and then set it as your textView's exclusionPaths:

textView.textContainer.exclusionPaths = [pathsAroundYourBezier];

Keep in mind that the exclusion paths are paths where text in the container will not be displayed. Apple documentation here: https://developer.apple.com/documentation/uikit/nstextcontainer/1444569-exclusionpaths

UPDATE DUE TO BUGS WITH EXCLUSION PATHS AT THE BEGINNING OF TEXTVIEW'S:

I've come up with a way to find where in a path text can fit.

Usage:

    let pathVisible = rectPathSharpU(CGSize(width: 100, height: 125), origin: CGPoint(x: 0, y: 0))
let shapeLayer = CAShapeLayer()
shapeLayer.path = pathVisible.cgPath
shapeLayer.strokeColor = UIColor.green.cgColor
shapeLayer.backgroundColor = UIColor.clear.cgColor
shapeLayer.lineWidth = 3
self.view.layer.addSublayer(shapeLayer)

let path = rectPathSharpU(CGSize(width: 100, height: 125), origin: CGPoint(x: 0, y: 0))
let fittingRect = findFirstRect(path: path, thatFits: "A".size())!
print("fittingRect: \(fittingRect)")
let label = UILabel.init(frame: fittingRect)
label.text = "A"
self.view.addSubview(label)

Output:

There may be cases with curved paths that will need to be taken into account, perhaps by iterating through every y point in a path bounds until a sizable space is found.

The function to find the first fitting rect:

func findFirstRect(path: UIBezierPath, thatFits: CGSize) -> CGRect? {
let points = path.cgPath.points
allPoints: for point in points {
var checkpoint = point
var size = CGSize(width: 0, height: 0)
thisPoint: while size.width <= path.bounds.width {
if path.contains(checkpoint) && path.contains(CGPoint.init(x: checkpoint.x + thatFits.width, y: checkpoint.y + thatFits.height)) {
return CGRect(x: checkpoint.x, y: checkpoint.y, width: thatFits.width, height: thatFits.height)
} else {
checkpoint.x += 1
size.width += 1
continue thisPoint
}
}
}
return nil
}

Extension for finding string size:

extension String {
func size(width:CGFloat = 220.0, font: UIFont = UIFont.systemFont(ofSize: 17.0, weight: .regular)) -> CGSize {
let label:UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: width, height: CGFloat.greatestFiniteMagnitude))
label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.byWordWrapping
label.font = font
label.text = self

label.sizeToFit()

return CGSize(width: label.frame.width, height: label.frame.height)
}
}

Creating the test path:

func rectPathSharpU(_ size: CGSize, origin: CGPoint) -> UIBezierPath {

// Initialize the path.
let path = UIBezierPath()

// Specify the point that the path should start get drawn.
path.move(to: CGPoint(x: origin.x, y: origin.y))
// add lines to path
path.addLine(to: CGPoint(x: (size.width / 3) + origin.x, y: (size.height / 3 * 2) + origin.y))
path.addLine(to: CGPoint(x: (size.width / 3 * 2) + origin.x, y: (size.height / 3 * 2) + origin.y))
path.addLine(to: CGPoint(x: size.width + origin.x, y: origin.y))
path.addLine(to: CGPoint(x: (size.width) + origin.x, y: size.height + origin.y))
path.addLine(to: CGPoint(x: origin.x, y: size.height + origin.y))

// Close the path. This will create the last line automatically.
path.close()

return path
}

If this doesn't work for paths with a lot of arcs like your picture example, please post the actual path data so I can test with that.

Bonus: I also created a function to find the widest section of a symmetric path, though height isn't taken into account. Though it may be useful:

func findWidestY(path: UIBezierPath) -> CGRect {
var widestSection = CGRect(x: 0, y: 0, width: 0, height: 0)
let points = path.cgPath.points
allPoints: for point in points {
var checkpoint = point
var size = CGSize(width: 0, height: 0)
thisPoint: while size.width <= path.bounds.width {
if path.contains(checkpoint) {
checkpoint.x += 1
size.width += 1
continue thisPoint
} else {
if size.width > widestSection.width {
widestSection = CGRect(x: point.x, y: point.y, width: size.width, height: 1)
}
break thisPoint
}
}
}
return widestSection
}

In HTML5, can I make an irregular shaped text box?

If you're able to provide boxes which describe the shape, you might be able to use CSS3 Regions. Availability in your target browsers might be pretty poor though. This is probably the spec Šime mentioned.

SVG also claims support for this in an SVG 1.2 working draft. I'm not clear whether this has made it to a W3 Recommendation, or whether it's actually supported in browsers.

Update: the answers to this question suggest the SVG approach won't work right now.

Rendering CoreText within an irregular shape

I wrote a blog post about achieving text wrap with Core Text:

http://blog.amyworrall.com/post/11098565269/text-wrap-with-core-text

The feature is new in iOS 4.3 and MacOS X Lion. You can now firstly draw inside non-rectangular paths, and secondly pass in other paths to mask the flow (i.e. be the holes you wrap around).



Related Topics



Leave a reply



Submit