使用Perl将CSS样式属性转换为HTML属性

4
真正的背景:我们有一个PDFMaker(HTMLDoc),可以将html转换为pdf。HTMLDoc无法始终从客户提供给我们的html中选择我们需要的样式。因此,我试图将诸如style =“width:80px; height:90px;”之类的内容转换为height = 80 width = 90。

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

我可能是错误的方法,但我想不出更快或更聪明的方法来在Perl中完成这项任务。任何帮助都将不胜感激!

注意:我尝试更改此特定脚本的唯一属性是“高度”,“宽度”和“边框”,因为我们的客户使用工具自动将样式应用于他们使用WYSIWYG风格编辑器拖动的元素。显然,使用正则表达式从许多地方剥离这些属性相当有效,因为您只需让表格单元格按其内容调整大小即可,这看起来很好,但我认为处理问题的更快速的方法就是用“宽度”、“高度”和“边框”属性替换这三个属性,它们的行为大部分与其css对应物相同(除了CSS允许您实际自定义边框的宽度、颜色和样式,但他们仅使用固定的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;

虽然不完全相同,但是https://dev59.com/m0jSa4cB1Zd3GeqPJN9t#1271680 可能会给你一些想法。 - Sinan Ünür
3个回答

5

由于以下几个原因,你将会在这方面遇到很大的困难:

  • 大多数可以用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语言,有一些标准的口号需要遵循,例如始终使用strict和warnings,尽可能使用命名匹配而不是$1、$2等。但我试图限制我的建议只涉及那些可以立即推动您的解决方案前进的内容。

我应该明确指出,我想要转换的唯一属性是边框、高度和宽度。其他所有属性都不重要,我们也会手动处理。 - NateDSaint
1
另外,我应该注明,我并不是试图创建一个完全了解哪些标签具有哪些样式属性并知道如何处理它们的CSS渲染引擎。在我所提供的特定情况下,他们使用的工具仅规定单个表格单元格的大小,其他所有内容都带有ID或类,并使用外部样式表进行样式化,但无论如何都会被pdfmaker忽略。 - NateDSaint
分割是我遇到困难的地方,确保全部都被分割。至于使用正确的 Perl 格式,我最初尝试尽可能使用速记方式进行练习,但流式传输一个输入文件和输出文件证明很棘手,所以我只是采用了快速而不太规范的方法。感谢您的帮助! - NateDSaint

3
请查看CPAN上的HTML解析模块,例如HTML::TreeBuilderHTML::DOM甚至是XML模块,如XML::LibXML

以下是使用HTML::TreeBuilder的快速示例,它将向具有带有边框内容的样式属性的任何标记添加border="1"属性:

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"

除了这些模块,您还可以查看CSSCSS::DOM来帮助解析CSS部分。


感谢您的输入,看起来很棒!我会尝试在我们的服务器上设置这个,但现在我必须快速编写一个脚本方法。但这是一个更好的长期解决方案,谢谢! - NateDSaint

2

我不知道你对专有软件的立场如何,但是PrinceXML是目前最好的HTML转PDF工具。


1
我对专有软件没有问题,但我的老板有。我们通常尽可能多地使用开源软件。这并不一定是为了节省钱(因为集成的劳动力成本在大多数情况下会使其变得相当),而是为了将其与我们疯狂定制的Apache安装集成。 - NateDSaint

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接