【问题标题】:Convert CSS Style Attributes to HTML Attributes using Perl使用 Perl 将 CSS 样式属性转换为 HTML 属性
【发布时间】:2017-04-21 23:32:03
【问题描述】:

真正的快速背景:我们有一个 PDFMaker (HTMLDoc) 可以将 html 转换为 pdf。 HTMLDoc 不会始终从客户端提供给我们的 html 中获取我们需要的样式。因此,我试图转换诸如 style="width:80px;height:90px;" 之类的东西到高度=80 宽度=90。

到目前为止,我的尝试表明我对反向引用以及如何在 Perl Regex 中正确使用它们的理解有限。我可以获取输入文件并将其转换为输出文件,但它每行仅捕获一种“样式”,并且仅替换该 css 中的一个名称/值对。

我可能以错误的方式处理此问题,但我无法在 Perl 中找到更快或更智能的方法来执行此操作。任何帮助将不胜感激!

注意:我试图为这个特定脚本更改的唯一属性是“高度”、“宽度”和“边框”,因为我们的客户端使用了一种工具,该工具会自动将样式应用于他们通过所见即所得拖动的元素风格的编辑器。显然,使用正则表达式将这些从很多地方剥离出来效果很好,因为您只需让表格单元格根据其内容调整大小,这看起来还可以,但我想一个更快的方法来处理这个问题就是用“width”“height”和“border”属性替换这三个属性,它们的行为与它们的css对应物基本相同(除了CSS允许您实际自定义边框的宽度、颜色和样式,但它们都曾经use 是solid 1px,所以我可以添加一个条件来将“solid 1px”替换为“border=1”。我意识到这些并不完全等效,但对于这个应用程序来说,这将是一个步骤。

这是我目前得到的:

#!/usr/bin/perl
if (!@ARGV[0] || !@ARGV[1])
{
  print "Usage: converter.pl [input file] [output file] \n";
  exit;
}
open FILE, "<", @ARGV[0] or die $!;
open OUTFILE, ">", @ARGV[1] or die $!;
my $line;
my $guts;
while ( <FILE> ) {
  $line = $_ ;
  $line =~ /style=\"(.+)\"/;
  $guts = $1;
  $guts =~ /([a-zA-Z]+)\:([a-zA-Z0-9]+)\;/;
  $name = $1;
  $value = $2;
  $guts = $name."=".$value;
  $line =~ s/style=\"(.+)\"/$guts/g;
  print OUTFILE $line ;
}

exit;

注意:这不是家庭作业,不,我不是要你为我做我的工作,这最终会成为一个内部工具,它只是加快了我们传入的 html 的格式化过程,以便在 pdf 中正常工作我们有转换器。

更新

对于那些感兴趣的人,我得到了一个初始工作版本。这个只替换了宽度和高度,我们现在正在废弃的边框属性。但是如果有人想看看我们是怎么做到的,那就看看吧……

#!/usr/bin/perl

## NOTES ##
# This script was made to simply replace style attributes with their name/value pair equivalents as attributes.
# It was designed to replace width and height attributes on a metric buttload of table elements from client data we got.
# As such, it's not really designed to handle more than that, and only strips the unit "PX" from the values. 
# All of these can be modified in the second foreach loop, which checks for height and width. 

if (!@ARGV[0] || !@ARGV[1])
{
  print "Usage: quickvert.pl [input file] [output file] \n";
  exit;
}
open FILE, "<", @ARGV[0] or die $!;
open OUTFILE, ">", @ARGV[1] or die $!;
my $line;
my $guts;
my $count = 1;
while ( <FILE> ) {
  $line = $_ ;
  my (@match) = $line =~ /style=\"(.+?)\"/g;
  my $guts;
  my $newguts;
  foreach (@match) {
    #print $_ ."\n";
    $guts = $_;
    $guts =~ /([a-zA-Z]+)\:([a-zA-Z0-9]+)\;/;
    $newguts = "";
    foreach my $style (split(/;/,$guts)) {
      my ($name, $value) = split(/:/,$style);
      $value =~ s/px//g;
      if ( $name =~ m/height/g || $name =~ m/width/g ) {
      $newguts .= "$name='$value' ";
      } else {
      $newguts .= "";
      }
    }
    #print "replacing $guts with $newguts on line $count \n";
  $line =~ s/style=\"$guts\"/$newguts/i;
  }

  #print $newguts;



  print OUTFILE $line ;
  $count++;
}

exit;

【问题讨论】:

标签: html css perl


【解决方案1】:

由于以下几个原因,您将很难处理:

  • 大多数可以用 CSS 完成的事情不能用 HTML 属性完成。要解决这个问题,您要么必须忽略或尝试补偿边距和填充等内容......
  • HTML 属性和 CSS 之间对应的许多事物的行为实际上略有不同,您需要考虑到这一点。为了解决这个问题,您必须为每个差异编写特定的代码......
  • 由于 CSS 规则的应用方式,您基本上需要使用完整的 CSS 引擎来解析和应用所有规则,然后才能知道在元素/属性级别需要做什么。要解决这个问题,您可以忽略除内联样式之外的任何内容,但是...

这项工作几乎和为浏览器编写渲染引擎一样复杂。您可能能够处理一些特定的案例,但即使这样,您的成功率充其量也只是偶然的。

编辑:鉴于您非常具体的功能集,我可以就您的实施给您一些建议:

您希望在查找样式属性的值时不区分大小写并使用非贪婪匹配,即:

$line =~ /style=\"(.+?)\"/i;

这样您只能找到直到下一个双引号的内容,而不是直到最后一个双引号的行的全部内容。此外,如果找不到匹配项,您可能想跳过该行,所以:

next unless ($line =~ /style=\"(.+?)\"/i);

为了解析胆量,我会使用 split 而不是正则表达式:

my $newguts;
foreach my $style (split(/;/,$guts)) {
    my ($name, $value) = split(/:/,$style);
    $newguts .= "$name='$value' ";
}
$line =~ s/style=\"$guts\"/$newguts/i;

当然,这是 Perl 有一些标准的口头禅,例如始终使用严格和警告,尝试使用命名匹配而不是 $1$2 等,但我试图将我的建议限制在一些东西上这将立即推动您的解决方案。

【讨论】:

  • 我可能应该指定我想要转换的唯一属性是边框、高度和宽度。其他一切都不重要,反正我们都是手工完成的。
  • 另外,我应该评论一下,我并没有尝试创建一个 CSS 渲染引擎,它完全了解哪些标签具有哪些样式属性并了解处理它们的方式。在提供给我的特定情况下,他们使用的工具仅规定了单个表格单元格的大小,其他所有内容都被赋予了 ID 或类,并使用外部表格设置样式,在任何情况下都会被 pdfmaker 忽略。
  • 分裂是我遇到困难的地方,确保将他们全部击中。至于为 perl 使用正确的格式,我最初是为了练习而尽可能地简化它,但是流式传输输入文件和输出文件被证明是棘手的,所以我只是以快速而肮脏的方式来做。感谢您的帮助!
【解决方案2】:

查看CPAN 以了解HTML::TreeBuilderHTML::DOM 等HTML 解析模块,甚至XML::LibXML 等XML 模块。

下面是使用 HTML::TreeBuilder 的快速示例,它将 border="1" 属性添加到具有 style 属性和 border 的任何标签内容:

use strict;
use warnings;
use HTML::TreeBuilder;

my $data =q{
<html>
<head>
</head>
<body>
<h1>blah</h1>
<p style="color: red;">Red</p>
<span style="width:80px;height:90px;border: 1px solid #000000">Some text</span>
</body>
</html>
};

my $tree = HTML::TreeBuilder->new;
$tree->parse_content( $data );

for my $style ( $tree->look_down( sub { $_[0]->attr('style') } ) ) {
    my $prop = $style->attr( 'style' );
    $style->attr( 'border', 1 ) if $prop =~ m/border/;
}

say $tree->as_HTML;

这将重现 HTML,但仅将 border="1" 添加到 span 标记中。

与这些模块一致,您还可以查看 CSSCSS::DOM 以帮助解析 CSS 位。

【讨论】:

  • 感谢您的意见,它看起来棒极了!我会看看我们是否可以在我们的服务器上进行设置,但现在我必须在其中获得一个快速的脚本方法。但这是一个更好的长期解决方案,谢谢!
【解决方案3】:

我不知道您对专有软件的立场,但PrinceXML 是目前最好的 HTML 到 PDF 转换器。

【讨论】:

  • 我对专有软件没有问题,但我的老板有。我们通常使用尽可能多的开源项目。不一定是为了省钱(因为在大多数情况下,集成的人工成本最终会让它变得一文不值),而是为了将它与我们疯狂定制的 Apache 安装集成。
猜你喜欢
  • 1970-01-01
  • 2012-05-11
  • 2016-08-25
  • 2011-07-27
  • 1970-01-01
  • 2021-07-21
  • 2017-12-25
  • 1970-01-01
  • 2013-08-01
相关资源
最近更新 更多