Преобразование атрибутов стиля CSS в атрибуты HTML с использованием Perl - PullRequest
4 голосов
/ 27 августа 2009

Очень быстрый фон: У нас есть PDFMaker (HTMLDoc), который преобразует HTML в PDF. HTMLDoc не всегда выбирает нужные нам стили из HTML, предоставленного нам клиентом. Таким образом я пытаюсь преобразовать такие вещи, как style = "width: 80px; height: 90px;" к высоте = 80 к ширине = 90.

Моя попытка до сих пор показала мое ограниченное понимание обратных ссылок и того, как правильно их использовать во время Perl Regex. Я могу взять входной файл и преобразовать его в выходной файл, но он ловит только один «стиль» на строку и заменяет только одну пару имя / значение из этого CSS.

Я, вероятно, подхожу к этому неправильно, но я не могу придумать более быстрый или умный способ сделать это в Perl. Любая помощь будет принята с благодарностью!

ПРИМЕЧАНИЕ. Единственными атрибутами, которые я пытаюсь изменить для этого конкретного сценария, являются «высота», «ширина» и «граница», поскольку наш клиент использует инструмент, который автоматически применяет стили к элементам, которые они перетаскивают с помощью WYSIWYG. редактор стиля. Очевидно, что использование регулярных выражений для удаления их из множества мест работает довольно хорошо, поскольку вы просто позволяете ячейкам таблицы измеряться по их содержимому, что выглядит хорошо, но я подумал, что более быстрый способ справиться с проблемой - просто замените эти три атрибута атрибутами «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;

Ответы [ 3 ]

5 голосов
/ 27 августа 2009

Вам будет очень тяжело с этим по нескольким причинам:

  • Большинство вещей, которые можно сделать с помощью CSS, нельзя сделать с помощью атрибутов HTML. Чтобы справиться с этим, вам придется либо игнорировать, либо пытаться компенсировать такие вещи, как поля и отступы и т. Д. *
  • Многие вещи, которые соответствуют атрибутам HTML и CSS, на самом деле ведут себя немного по-разному, и вам нужно будет это учитывать. Чтобы справиться с этим, вам придется написать конкретный код для каждого различия ...
  • Из-за того, как применяются правила CSS, вам, в основном, нужно использовать полный движок CSS для анализа и применения всех правил, прежде чем вы узнаете, что нужно сделать на уровне элемента / атрибута. Чтобы справиться с этим, вы можете просто игнорировать все, кроме встроенных стилей, но ...

Эта работа почти такая же сложная, как написание движка рендеринга для браузера. Возможно, вам удастся разобраться с несколькими конкретными случаями, но даже в этом случае ваш показатель успеха будет в лучшем случае случайным.

РЕДАКТИРОВАТЬ: Учитывая ваш очень специфический набор функций, я могу дать вам небольшой совет по вашей реализации:

Вы хотите быть нечувствительными к регистру и использовать не жадное совпадение при поиске значения атрибута style, т. Е .:

$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 и т. Д., Но я пытаюсь ограничить свой совет вещами это сразу же продвинет ваше решение вперед.

3 голосов
/ 27 августа 2009

Посмотрите на CPAN для модулей синтаксического анализа HTML, таких как HTML :: TreeBuilder , HTML :: DOM или даже XML-модулей, таких как XML: : LibXML .

Ниже приведен быстрый пример использования HTML :: TreeBuilder, который добавляет атрибут border = "1" к любому тегу, который имеет атрибут style с border content:

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 .

В унисон с этими модулями вы также можете взглянуть на CSS и CSS :: DOM , чтобы помочь разобрать бит CSS.

2 голосов
/ 27 августа 2009

Я не знаю, как вы относитесь к проприетарному программному обеспечению, но PrinceXML - лучший доступный конвертер HTML в PDF.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...