2 * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 #define _USE_MATH_DEFINES 1
28 #include "GraphicsContext.h"
32 #include "AffineTransform.h"
35 #include <wtf/MathExtras.h>
38 #include "KRenderingDeviceQuartz.h"
41 #include <GraphicsContextPlatformPrivate.h> // FIXME: Temporary.
47 GraphicsContext::GraphicsContext(CGContextRef cgContext)
48 : m_common(createGraphicsContextPrivate())
49 , m_data(new GraphicsContextPlatformPrivate(cgContext))
51 setPaintingDisabled(!cgContext);
54 GraphicsContext::~GraphicsContext()
56 destroyGraphicsContextPrivate(m_common);
60 void GraphicsContext::setFocusRingClip(const IntRect& r)
62 // This method only exists to work around bugs in Mac focus ring clipping.
63 m_data->m_focusRingClip = r;
66 void GraphicsContext::clearFocusRingClip()
68 // This method only exists to work around bugs in Mac focus ring clipping.
69 m_data->m_focusRingClip = IntRect();
72 CGContextRef GraphicsContext::platformContext() const
74 ASSERT(!paintingDisabled());
75 ASSERT(m_data->m_cgContext);
76 return m_data->m_cgContext;
79 static void setCGFillColor(CGContextRef context, const Color& color)
81 CGFloat red, green, blue, alpha;
82 color.getRGBA(red, green, blue, alpha);
83 CGContextSetRGBFillColor(context, red, green, blue, alpha);
86 static void setCGStrokeColor(CGContextRef context, const Color& color)
88 CGFloat red, green, blue, alpha;
89 color.getRGBA(red, green, blue, alpha);
90 CGContextSetRGBStrokeColor(context, red, green, blue, alpha);
93 void GraphicsContext::savePlatformState()
95 // Note: Do not use this function within this class implementation, since we want to avoid the extra
96 // save of the secondary context (in GraphicsContextPlatformPrivate.h).
97 CGContextSaveGState(platformContext());
101 void GraphicsContext::restorePlatformState()
103 // Note: Do not use this function within this class implementation, since we want to avoid the extra
104 // restore of the secondary context (in GraphicsContextPlatformPrivate.h).
105 CGContextRestoreGState(platformContext());
109 // Draws a filled rectangle with a stroked border.
110 void GraphicsContext::drawRect(const IntRect& rect)
112 if (paintingDisabled())
115 CGContextRef context = platformContext();
117 if (fillColor().alpha()) {
118 setCGFillColor(context, fillColor());
119 CGContextFillRect(context, rect);
122 if (pen().style() != Pen::NoPen) {
123 setCGFillColor(context, pen().color());
125 FloatRect(rect.x(), rect.y(), rect.width(), 1),
126 FloatRect(rect.x(), rect.bottom() - 1, rect.width(), 1),
127 FloatRect(rect.x(), rect.y() + 1, 1, rect.height() - 2),
128 FloatRect(rect.right() - 1, rect.y() + 1, 1, rect.height() - 2)
130 CGContextFillRects(context, rects, 4);
134 // This is only used to draw borders.
135 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
137 if (paintingDisabled())
140 Pen::PenStyle penStyle = pen().style();
141 if (penStyle == Pen::NoPen)
143 float width = pen().width();
147 FloatPoint p1 = point1;
148 FloatPoint p2 = point2;
149 bool isVerticalLine = (p1.x() == p2.x());
151 // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic
152 // works out. For example, with a border width of 3, KHTML will pass us (y1+y2)/2, e.g.,
153 // (50+53)/2 = 103/2 = 51 when we want 51.5. It is always true that an even width gave
154 // us a perfect position, but an odd width gave us a position that is off by exactly 0.5.
155 if (penStyle == Pen::DotLine || penStyle == Pen::DashLine) {
156 if (isVerticalLine) {
165 if (((int)width) % 2) {
166 if (isVerticalLine) {
167 // We're a vertical line. Adjust our x.
171 // We're a horizontal line. Adjust our y.
183 patWidth = (int)width;
186 patWidth = 3 * (int)width;
190 CGContextRef context = platformContext();
192 CGContextSaveGState(context);
194 setCGStrokeColor(context, pen().color());
196 CGContextSetShouldAntialias(context, false);
199 // Do a rect fill of our endpoints. This ensures we always have the
200 // appearance of being a border. We then draw the actual dotted/dashed line.
201 setCGFillColor(context, pen().color());
202 if (isVerticalLine) {
203 CGContextFillRect(context, FloatRect(p1.x() - width / 2, p1.y() - width, width, width));
204 CGContextFillRect(context, FloatRect(p2.x() - width / 2, p2.y(), width, width));
206 CGContextFillRect(context, FloatRect(p1.x() - width, p1.y() - width / 2, width, width));
207 CGContextFillRect(context, FloatRect(p2.x(), p2.y() - width / 2, width, width));
210 // Example: 80 pixels with a width of 30 pixels.
211 // Remainder is 20. The maximum pixels of line we could paint
212 // will be 50 pixels.
213 int distance = (isVerticalLine ? (point2.y() - point1.y()) : (point2.x() - point1.x())) - 2*(int)width;
214 int remainder = distance % patWidth;
215 int coverage = distance - remainder;
216 int numSegments = coverage / patWidth;
218 float patternOffset = 0;
219 // Special case 1px dotted borders for speed.
223 bool evenNumberOfSegments = numSegments % 2 == 0;
225 evenNumberOfSegments = !evenNumberOfSegments;
226 if (evenNumberOfSegments) {
228 patternOffset += patWidth - remainder;
229 patternOffset += remainder / 2;
231 patternOffset = patWidth / 2;
234 patternOffset = (patWidth - remainder)/2;
238 const CGFloat dottedLine[2] = { patWidth, patWidth };
239 CGContextSetLineDash(context, patternOffset, dottedLine, 2);
242 CGContextSetLineWidth(context, width);
244 CGContextBeginPath(context);
245 CGContextMoveToPoint(context, p1.x(), p1.y());
246 CGContextAddLineToPoint(context, p2.x(), p2.y());
248 CGContextStrokePath(context);
250 CGContextRestoreGState(context);
253 // This method is only used to draw the little circles used in lists.
254 void GraphicsContext::drawEllipse(const IntRect& rect)
256 // FIXME: CG added CGContextAddEllipseinRect in Tiger, so we should be able to quite easily draw an ellipse.
257 // This code can only handle circles, not ellipses. But khtml only
258 // uses it for circles.
259 ASSERT(rect.width() == rect.height());
261 if (paintingDisabled())
264 CGContextRef context = platformContext();
265 CGContextBeginPath(context);
266 float r = (float)rect.width() / 2;
267 CGContextAddArc(context, rect.x() + r, rect.y() + r, r, 0, 2*M_PI, true);
268 CGContextClosePath(context);
270 if (fillColor().alpha()) {
271 setCGFillColor(context, fillColor());
272 if (pen().style() != Pen::NoPen) {
274 setCGStrokeColor(context, pen().color());
275 unsigned penWidth = pen().width();
278 CGContextSetLineWidth(context, penWidth);
279 CGContextDrawPath(context, kCGPathFillStroke);
281 CGContextFillPath(context);
282 } else if (pen().style() != Pen::NoPen) {
283 setCGStrokeColor(context, pen().color());
284 unsigned penWidth = pen().width();
287 CGContextSetLineWidth(context, penWidth);
288 CGContextStrokePath(context);
293 void GraphicsContext::drawArc(const IntRect& rect, float thickness, int startAngle, int angleSpan)
295 if (paintingDisabled())
298 CGContextRef context = platformContext();
299 CGContextSaveGState(context);
300 CGContextBeginPath(context);
301 CGContextSetShouldAntialias(context, false);
305 float w = (float)rect.width();
306 float h = (float)rect.height();
307 float scaleFactor = h / w;
308 float reverseScaleFactor = w / h;
311 scale(FloatSize(1, scaleFactor));
313 float hRadius = w / 2;
314 float vRadius = h / 2;
315 float fa = startAngle;
316 float falen = fa + angleSpan;
317 float start = -fa * M_PI/180;
318 float end = -falen * M_PI/180;
319 CGContextAddArc(context, x + hRadius, (y + vRadius) * reverseScaleFactor, hRadius, start, end, true);
322 scale(FloatSize(1, reverseScaleFactor));
324 if (pen().style() == Pen::NoPen) {
325 setCGStrokeColor(context, fillColor());
326 CGContextSetLineWidth(context, thickness);
327 CGContextStrokePath(context);
329 Pen::PenStyle penStyle = pen().style();
330 float width = pen().width();
340 patWidth = (int)(width / 2);
343 patWidth = 3 * (int)(width / 2);
347 CGContextSaveGState(context);
348 setCGStrokeColor(context, pen().color());
351 // Example: 80 pixels with a width of 30 pixels.
352 // Remainder is 20. The maximum pixels of line we could paint
353 // will be 50 pixels.
355 if (hRadius == vRadius)
356 distance = (int)(M_PI * hRadius) / 2;
357 else // We are elliptical and will have to estimate the distance
358 distance = (int)(M_PI * sqrt((hRadius * hRadius + vRadius * vRadius) / 2)) / 2;
360 int remainder = distance % patWidth;
361 int coverage = distance - remainder;
362 int numSegments = coverage / patWidth;
364 float patternOffset = 0;
365 // Special case 1px dotted borders for speed.
369 bool evenNumberOfSegments = numSegments % 2 == 0;
371 evenNumberOfSegments = !evenNumberOfSegments;
372 if (evenNumberOfSegments) {
374 patternOffset += patWidth - remainder;
375 patternOffset += remainder / 2;
377 patternOffset = patWidth / 2;
380 patternOffset = (patWidth - remainder) / 2;
384 const CGFloat dottedLine[2] = { patWidth, patWidth };
385 CGContextSetLineDash(context, patternOffset, dottedLine, 2);
388 CGContextSetLineWidth(context, width);
389 CGContextStrokePath(context);
390 CGContextRestoreGState(context);
393 CGContextRestoreGState(context);
396 void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias)
398 if (paintingDisabled())
404 CGContextRef context = platformContext();
406 CGContextSaveGState(context);
408 CGContextSetShouldAntialias(context, shouldAntialias);
410 CGContextBeginPath(context);
411 CGContextMoveToPoint(context, points[0].x(), points[0].y());
412 for (size_t i = 1; i < npoints; i++)
413 CGContextAddLineToPoint(context, points[i].x(), points[i].y());
414 CGContextClosePath(context);
416 if (fillColor().alpha()) {
417 setCGFillColor(context, fillColor());
418 CGContextEOFillPath(context);
421 if (pen().style() != Pen::NoPen) {
422 setCGStrokeColor(context, pen().color());
423 CGContextSetLineWidth(context, pen().width());
424 CGContextStrokePath(context);
427 CGContextRestoreGState(context);
430 void GraphicsContext::fillRect(const IntRect& rect, const Color& color)
432 if (paintingDisabled())
435 CGContextRef context = platformContext();
436 setCGFillColor(context, color);
437 CGContextFillRect(context, rect);
441 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color)
443 if (paintingDisabled())
446 CGContextRef context = platformContext();
447 setCGFillColor(context, color);
448 CGContextFillRect(context, rect);
452 void GraphicsContext::clip(const IntRect& rect)
454 if (paintingDisabled())
456 CGContextClipToRect(platformContext(), rect);
460 void GraphicsContext::addRoundedRectClip(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight,
461 const IntSize& bottomLeft, const IntSize& bottomRight)
463 if (paintingDisabled())
466 // Need sufficient width and height to contain these curves. Sanity check our top/bottom
467 // values and our width/height values to make sure the curves can all fit.
468 int requiredWidth = max(topLeft.width() + topRight.width(), bottomLeft.width() + bottomRight.width());
469 if (requiredWidth > rect.width())
471 int requiredHeight = max(topLeft.height() + bottomLeft.height(), topRight.height() + bottomRight.height());
472 if (requiredHeight > rect.height())
478 // OK, the curves can fit.
479 CGContextRef context = platformContext();
481 // Add the four ellipses to the path. Technically this really isn't good enough, since we could end up
482 // not clipping the other 3/4 of the ellipse we don't care about. We're relying on the fact that for
483 // normal use cases these ellipses won't overlap one another (or when they do the curvature of one will
484 // be subsumed by the other).
485 CGContextAddEllipseInRect(context, CGRectMake(rect.x(), rect.y(), topLeft.width() * 2, topLeft.height() * 2));
486 CGContextAddEllipseInRect(context, CGRectMake(rect.right() - topRight.width() * 2, rect.y(),
487 topRight.width() * 2, topRight.height() * 2));
488 CGContextAddEllipseInRect(context, CGRectMake(rect.x(), rect.bottom() - bottomLeft.height() * 2,
489 bottomLeft.width() * 2, bottomLeft.height() * 2));
490 CGContextAddEllipseInRect(context, CGRectMake(rect.right() - bottomRight.width() * 2,
491 rect.bottom() - bottomRight.height() * 2,
492 bottomRight.width() * 2, bottomRight.height() * 2));
494 // Now add five rects (one for each edge rect in between the rounded corners and one for the interior).
495 CGContextAddRect(context, CGRectMake(rect.x() + topLeft.width(), rect.y(),
496 rect.width() - topLeft.width() - topRight.width(),
497 max(topLeft.height(), topRight.height())));
498 CGContextAddRect(context, CGRectMake(rect.x() + bottomLeft.width(),
499 rect.bottom() - max(bottomLeft.height(), bottomRight.height()),
500 rect.width() - bottomLeft.width() - bottomRight.width(),
501 max(bottomLeft.height(), bottomRight.height())));
502 CGContextAddRect(context, CGRectMake(rect.x(), rect.y() + topLeft.height(),
503 max(topLeft.width(), bottomLeft.width()), rect.height() - topLeft.height() - bottomLeft.height()));
504 CGContextAddRect(context, CGRectMake(rect.right() - max(topRight.width(), bottomRight.width()),
505 rect.y() + topRight.height(),
506 max(topRight.width(), bottomRight.width()), rect.height() - topRight.height() - bottomRight.height()));
507 CGContextAddRect(context, CGRectMake(rect.x() + max(topLeft.width(), bottomLeft.width()),
508 rect.y() + max(topLeft.height(), topRight.height()),
509 rect.width() - max(topLeft.width(), bottomLeft.width()) - max(topRight.width(), bottomRight.width()),
510 rect.height() - max(topLeft.height(), topRight.height()) - max(bottomLeft.height(), bottomRight.height())));
511 CGContextClip(context);
514 void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
516 if (paintingDisabled())
520 CGContextRef context = platformContext();
523 CGContextAddEllipseInRect(context, CGRectMake(rect.x(), rect.y(), rect.width(), rect.height()));
524 // Add inner ellipse.
525 CGContextAddEllipseInRect(context, CGRectMake(rect.x() + thickness, rect.y() + thickness,
526 rect.width() - (thickness * 2), rect.height() - (thickness * 2)));
528 CGContextEOClip(context);
532 KRenderingDeviceContext* GraphicsContext::createRenderingDeviceContext()
534 return new KRenderingDeviceContextQuartz(platformContext());
538 void GraphicsContext::beginTransparencyLayer(float opacity)
540 if (paintingDisabled())
542 CGContextRef context = platformContext();
543 CGContextSaveGState(context);
544 CGContextSetAlpha(context, opacity);
545 CGContextBeginTransparencyLayer(context, 0);
546 m_data->beginTransparencyLayer();
549 void GraphicsContext::endTransparencyLayer()
551 if (paintingDisabled())
553 CGContextRef context = platformContext();
554 CGContextEndTransparencyLayer(context);
555 CGContextRestoreGState(context);
556 m_data->endTransparencyLayer();
559 void GraphicsContext::setShadow(const IntSize& size, int blur, const Color& color)
561 if (paintingDisabled())
563 // Check for an invalid color, as this means that the color was not set for the shadow
564 // and we should therefore just use the default shadow color.
565 CGContextRef context = platformContext();
566 if (!color.isValid())
567 CGContextSetShadow(context, CGSizeMake(size.width(), -size.height()), blur); // y is flipped.
569 CGColorRef colorCG = cgColor(color);
570 CGContextSetShadowWithColor(context,
571 CGSizeMake(size.width(), -size.height()), // y is flipped.
574 CGColorRelease(colorCG);
578 void GraphicsContext::clearShadow()
580 if (paintingDisabled())
582 CGContextSetShadowWithColor(platformContext(), CGSizeZero, 0, 0);
585 void GraphicsContext::setLineWidth(float width)
587 if (paintingDisabled())
589 CGContextSetLineWidth(platformContext(), width);
592 void GraphicsContext::setMiterLimit(float limit)
594 if (paintingDisabled())
596 CGContextSetMiterLimit(platformContext(), limit);
599 void GraphicsContext::setAlpha(float alpha)
601 if (paintingDisabled())
603 CGContextSetAlpha(platformContext(), alpha);
606 void GraphicsContext::clearRect(const FloatRect& r)
608 if (paintingDisabled())
610 CGContextClearRect(platformContext(), r);
613 void GraphicsContext::strokeRect(const FloatRect& r, float lineWidth)
615 if (paintingDisabled())
617 CGContextStrokeRectWithWidth(platformContext(), r, lineWidth);
620 void GraphicsContext::setLineCap(LineCap cap)
622 if (paintingDisabled())
626 CGContextSetLineCap(platformContext(), kCGLineCapButt);
629 CGContextSetLineCap(platformContext(), kCGLineCapRound);
632 CGContextSetLineCap(platformContext(), kCGLineCapSquare);
637 void GraphicsContext::setLineJoin(LineJoin join)
639 if (paintingDisabled())
643 CGContextSetLineJoin(platformContext(), kCGLineJoinMiter);
646 CGContextSetLineJoin(platformContext(), kCGLineJoinRound);
649 CGContextSetLineJoin(platformContext(), kCGLineJoinBevel);
654 void GraphicsContext::clip(const Path& path)
656 if (paintingDisabled())
658 CGContextRef context = platformContext();
659 CGContextBeginPath(context);
660 CGContextAddPath(context, path.platformPath());
661 CGContextClip(context);
665 void GraphicsContext::scale(const FloatSize& size)
667 if (paintingDisabled())
669 CGContextScaleCTM(platformContext(), size.width(), size.height());
673 void GraphicsContext::rotate(float angle)
675 if (paintingDisabled())
677 CGContextRotateCTM(platformContext(), angle);
678 m_data->rotate(angle);
681 void GraphicsContext::translate(float x, float y)
683 if (paintingDisabled())
685 CGContextTranslateCTM(platformContext(), x, y);
686 m_data->translate(x, y);
689 void GraphicsContext::concatCTM(const AffineTransform& transform)
691 if (paintingDisabled())
693 CGContextConcatCTM(platformContext(), transform);
694 m_data->concatCTM(transform);
697 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect)
699 // It is not enough just to round to pixels in device space. The rotation part of the
700 // affine transform matrix to device space can mess with this conversion if we have a
701 // rotating image like the hands of the world clock widget. We just need the scale, so
702 // we get the affine transform matrix and extract the scale.
703 CGAffineTransform deviceMatrix = CGContextGetUserSpaceToDeviceSpaceTransform(platformContext());
704 float deviceScaleX = sqrtf(deviceMatrix.a * deviceMatrix.a + deviceMatrix.b * deviceMatrix.b);
705 float deviceScaleY = sqrtf(deviceMatrix.c * deviceMatrix.c + deviceMatrix.d * deviceMatrix.d);
707 CGPoint deviceOrigin = CGPointMake(rect.x() * deviceScaleX, rect.y() * deviceScaleY);
708 CGPoint deviceLowerRight = CGPointMake((rect.x() + rect.width()) * deviceScaleX,
709 (rect.y() + rect.height()) * deviceScaleY);
711 deviceOrigin.x = roundf(deviceOrigin.x);
712 deviceOrigin.y = roundf(deviceOrigin.y);
713 deviceLowerRight.x = roundf(deviceLowerRight.x);
714 deviceLowerRight.y = roundf(deviceLowerRight.y);
716 // Don't let the height or width round to 0 unless either was originally 0
717 if (deviceOrigin.y == deviceLowerRight.y && rect.height() != 0)
718 deviceLowerRight.y += 1;
719 if (deviceOrigin.x == deviceLowerRight.x && rect.width() != 0)
720 deviceLowerRight.x += 1;
722 FloatPoint roundedOrigin = FloatPoint(deviceOrigin.x / deviceScaleX, deviceOrigin.y / deviceScaleY);
723 FloatPoint roundedLowerRight = FloatPoint(deviceLowerRight.x / deviceScaleX, deviceLowerRight.y / deviceScaleY);
724 return FloatRect(roundedOrigin, roundedLowerRight - roundedOrigin);
727 void GraphicsContext::drawLineForText(const IntPoint& point, int yOffset, int width, bool printing)
729 if (paintingDisabled())
732 // Note: This function assumes that point.x and point.y are integers (and that's currently always the case).
734 float y = point.y() + yOffset;
736 // Leave 1.0 in user space between the baseline of the text and the top of the underline.
737 // FIXME: Is this the right distance for space above the underline? Even for thick underlines on large sized text?
740 float thickness = pen().width();
742 // When printing, use a minimum thickness of 0.5 in user space.
743 // See bugzilla bug 4255 for details of why 0.5 is the right minimum thickness to use while printing.
747 // When printing, use antialiasing instead of putting things on integral pixel boundaries.
749 // On screen, use a minimum thickness of 1.0 in user space (later rounded to an integral number in device space).
753 // On screen, round all parameters to integer boundaries in device space.
754 CGRect lineRect = roundToDevicePixels(FloatRect(x, y, width, thickness));
755 x = lineRect.origin.x;
756 y = lineRect.origin.y;
757 width = (int)(lineRect.size.width);
758 thickness = lineRect.size.height;
761 // FIXME: How about using a rectangle fill instead of drawing a line?
762 CGContextSaveGState(platformContext());
764 setCGStrokeColor(platformContext(), pen().color());
766 CGContextSetLineWidth(platformContext(), thickness);
767 CGContextSetShouldAntialias(platformContext(), printing);
769 float halfThickness = thickness / 2;
771 CGPoint linePoints[2];
772 linePoints[0].x = x + halfThickness;
773 linePoints[0].y = y + halfThickness;
774 linePoints[1].x = x + width - halfThickness;
775 linePoints[1].y = y + halfThickness;
776 CGContextStrokeLineSegments(platformContext(), linePoints, 2);
778 CGContextRestoreGState(platformContext());
781 void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
783 if (paintingDisabled())
786 CFURLRef urlRef = link.createCFURL();
788 CGContextRef context = platformContext();
790 // Get the bounding box to handle clipping.
791 CGRect box = CGContextGetClipBoundingBox(context);
793 IntRect intBox((int)box.origin.x, (int)box.origin.y, (int)box.size.width, (int)box.size.height);
794 IntRect rect = destRect;
795 rect.intersect(intBox);
797 CGPDFContextSetURLForRect(context, urlRef,
798 CGRectApplyAffineTransform(rect, CGContextGetCTM(context)));
806 #endif // PLATFORM(CG)