perl 如何使用PDF图元绘制填充和未填充的圆?

0yycz8jy  于 2023-03-30  发布在  Perl
关注(0)|答案(3)|浏览(143)

在PostScript上画圆很容易,我很惊讶PDF显然没有携带同样的原语。有很多商业库可以做到这一点,但它不应该比这更简单吗?
也有一些技巧使用贝塞尔曲线,但你没有得到一个完美的圆,你必须画他们在连接段。我不需要一个完美的圆,只要它看起来接近完美。
我这样做是作为PDF-EasyPDF Perl模块的一个补充,但语言不是我需要帮助的部分。

elcex8rz

elcex8rz1#

PDF的图元中没有圆,只有边框。PDF成像模型是从PostScript成像模型构建的,它本身只提供使用arc/arcto图元的圆,这些图元本身是用边框实现的。
奇怪的是,我不得不在我正在编写的一些生成PDF的测试代码中调用这个确切的任务。下面是我是如何做到的:

private void DrawEllipse(PdfGraphics g, double xrad, double yrad)
    {
        const double magic = 0.551784;
        double xmagic = xrad * magic;
        double ymagic = yrad * magic;
        g.MoveTo(-xrad, 0);
        g.CurveTo(-xrad, ymagic, -xmagic, yrad, 0, yrad);
        g.CurveTo(xmagic, yrad, xrad, ymagic, xrad, 0);
        g.CurveTo(xrad, -ymagic, xmagic, -yrad, 0, -yrad);
        g.CurveTo(-xmagic, -yrad, -xrad, -ymagic, -xrad, 0);
    }

    private void DrawCircle(PdfGraphics g, double radius)
    {
        DrawEllipse(g, radius, radius);
    }

假设PdfGraphics是一个输出PDF命令的类,那么g.MoveTo(x, y)在内容流中将变成“x y m”。(自然是PDF)。这假设圆或椭圆将在原点绘制。要将其移动到其他位置,首先做一个平移变换或修改代码以减去所需的原点。2该代码给出了大约1/1250(约0.08%)的最坏情况误差和1/2500(约0.04%)的平均值。

lsmepo6l

lsmepo6l2#

plinth的答案和我最终发现的是一样的。有很多棘手的数学问题,可以简化为一个神奇的常数,并将任务细分为四个独立的贝塞尔曲线。我需要在原始PDF命令中完成这一点,但过程是一样的。

  • 移动到第一条曲线的起点。这是圆心减去半径,无论你喜欢哪个方向。
  • 找出曲线的终点(代码中的$x3$y3值),下标来自大多数人使用的贝塞尔曲线控制点标签。
  • 找出控制点,这就是$magic值出现的地方。
  • 当你完成一个线段时,做下一个线段。四个线段没有什么特别的,除了在笛卡尔坐标系中很好地工作,只需要加法和减法。
  • 如果需要填充,则以f结束,以绘制刚刚创建的路径的内部。

我可以做一些重构,但是当我使用这个的时候,很容易看出我通过单独的代码块得到了正确的标志。这是我添加到PDF::EasyPDF的子例程:

sub make_magic_circle
    {
    my( $pdf, # PDF::EasyPDF object
        $center,
        $r   # radius
        ) = @_;

    my( $xc, $yc ) = $center->xy;
    
    my $magic = $r * 0.552;
    my( $x0p, $y0p ) = ( $xc - $r, $yc );
    $pdf->{stream} .= "$x0p $y0p m\n";
    
    {
    ( $x0p, $y0p ) = ( $xc - $r, $yc );
    my( $x1, $y1 ) = ( $x0p,               $y0p + $magic );
    my( $x2, $y2 ) = ( $x0p + $r - $magic, $y0p + $r     );
    my( $x3, $y3 ) = ( $x0p + $r,          $y0p + $r     );
    $pdf->{stream} .= "$x1 $y1 $x2 $y2 $x3 $y3 c\n";
    }
    
    {
    ( $x0p, $y0p ) = ( $xc, $yc + $r );
    my( $x1, $y1 ) = ( $x0p + $magic, $y0p               );
    my( $x2, $y2 ) = ( $x0p + $r,     $y0p - $r + $magic );
    my( $x3, $y3 ) = ( $x0p + $r,     $y0p - $r          );
    $pdf->{stream} .= "$x1 $y1 $x2 $y2 $x3 $y3 c\n";
    }
    
    {
    ( $x0p, $y0p ) = ( $xc + $r, $yc );
    my( $x1, $y1 ) = ( $x0p,               $y0p - $magic );
    my( $x2, $y2 ) = ( $x0p - $r + $magic, $y0p - $r     );
    my( $x3, $y3 ) = ( $x0p - $r,          $y0p - $r     );
    $pdf->{stream} .= "$x1 $y1 $x2 $y2 $x3 $y3 c\n";
    }
    
    {
    ( $x0p, $y0p ) = ( $xc, $yc - $r );
    my( $x1, $y1 ) = ( $x0p - $magic,  $y0p               );
    my( $x2, $y2 ) = ( $x0p - $r,      $y0p + $r - $magic );
    my( $x3, $y3 ) = ( $x0p - $r,      $y0p + $r          );
    $pdf->{stream} .= "$x1 $y1 $x2 $y2 $x3 $y3 c\n";
    }
    
    $pdf->{stream} .= "f\n";
    }
eoxn13cs

eoxn13cs3#

这两个其他的答案都是完美的,但我只想为实心填充(因此没有笔画) 的特定情况添加一个小技巧。
简单地画一条零长度的线,线的粗细是圆的直径,但将“线帽”设置为rounded
这显然不是椭圆、圆弧、线段和许多相关形状的好解决方案......但它是一个很酷的技巧。

相关问题