2009-08-26 7 views
4

Réel fond rapide: Nous avons un PDFMaker (HTMLDoc) qui convertit html en pdf. HTMLDoc ne reprend pas systématiquement les styles dont nous avons besoin à partir du code HTML qui nous est fourni par le client. Ainsi, j'essaie de convertir des choses telles que style = "width: 80px; height: 90px;" à hauteur = 80 largeur = 90. Ma tentative jusqu'ici a révélé ma compréhension limitée des références arrières et comment les utiliser correctement au cours de Perl Regex. Je peux prendre un fichier d'entrée et le convertir en un fichier de sortie, mais il attrape seulement un "style" par ligne, et ne remplace qu'une paire nom/valeur de ce CSS.Convertir les attributs de style CSS en attributs HTML en utilisant Perl

Je suis probablement en train d'aborder ce problème, mais je n'arrive pas à trouver une façon plus rapide et plus intelligente de le faire en Perl. Toute aide serait grandement appréciée! REMARQUE: Les seuls attributs que j'essaie de modifier pour ce script particulier sont "height", "width" et "border", car notre client utilise un outil qui applique automatiquement les styles aux éléments qu'ils traînent avec un Éditeur de style WYSIWYG. Évidemment, utiliser une regex pour les supprimer de beaucoup d'endroits fonctionne assez bien, car vous laissez simplement les cellules de la table être dimensionnées par leur contenu, ce qui semble correct, mais je me suis dit qu'une façon plus rapide de régler le problème serait de remplacez ces trois attributs par les attributs "width" "height" et "border", qui se comportent généralement de la même manière que leurs équivalents css (sauf que CSS vous permet de personnaliser la largeur, la couleur et le style de la bordure, mais l'utilisation est solide 1px, donc je peux ajouter une condition pour remplacer "1px solide" par "border = 1" Je me rends compte que ceux-ci ne sont pas totalement équivalents, mais pour cette application ce serait une étape).

Voici ce que j'ai jusqu'à présent:

#!/usr/bin/perl 
if ([email protected][0] || [email protected][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; 

Note: Ce n'est pas devoirs, et non je ne vous demande pas de faire mon travail pour moi, ce serait finir par être un outil interne cela a accéléré le processus de formatage de notre HTML entrant pour fonctionner correctement dans le convertisseur PDF que nous avons.

MISE À JOUR

Pour les personnes intéressées, je me suis une version initiale de travail. Celui-ci ne remplace que la largeur et la hauteur, l'attribut de bordure que nous sommes en train de scrapper pour le moment. Mais si quelqu'un voulait voir comment nous l'avons fait, un coup d'oeil ...

#!/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 ([email protected][0] || [email protected][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; 
+0

Pas exactement la même chose mais http://stackoverflow.com/questions/1271438/how-can-i-merge-css-definitions-in-files-into-inline-style-attributes-using-per/1271680 # 1271680 pourrait vous donner quelques idées. –

Répondre

5

Vous aurez un temps très difficile avec cela, pour plusieurs raisons:

  • La plupart des choses qui peuvent être accompli avec CSS ne peut pas être fait avec des attributs HTML. Pour résoudre ce problème, vous devez soit ignorer, soit essayer de compenser des éléments tels que les marges et le remplissage, etc ...
  • Beaucoup de choses qui correspondent entre les attributs HTML et les CSS se comportent légèrement différemment, et vous devrez prendre en compte ce. Pour traiter cela, vous devrez écrire un code spécifique pour chaque différence ...
  • En raison de la façon dont les règles CSS sont appliquées, vous devez utiliser un moteur CSS complet pour analyser et appliquer toutes les règles avant de savoir ce qui doit être fait au niveau élément/attribut. Pour faire face à cela, vous pouvez simplement ignorer tout sauf les styles en ligne, mais ...

Ce travail est presque aussi compliqué que d'écrire un moteur de rendu pour un navigateur. Vous pourriez être en mesure de traiter quelques cas spécifiques, mais même là, votre taux de réussite serait au hasard.

EDIT: Compte tenu de votre ensemble de fonctionnalités très spécifiques, je peux vous donner un peu de conseils sur votre mise en œuvre:

Vous voulez être insensible à la casse et utiliser un match non gourmand lors de la recherche de la valeur de l'attribut de style, à savoir:

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

Alors que vous trouvez que des choses à la très prochaine guillemet, pas tout le contenu de la ligne jusqu'à la dernière guillemet. En outre, vous voulez probablement sauter la ligne si le match ne se trouve pas, donc:

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

Pour l'analyse syntaxique des tripes, j'utiliser split au lieu de regex:

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

Bien sûr, étant Perl il y a des mantras standards tels que toujours utiliser strict et warnings, essayez d'utiliser des correspondances nommées plutôt que $1, $2, etc., mais j'essaie de restreindre mon conseil à des choses qui vont faire avancer votre solution tout de suite.

+0

Je devrais probablement spécifier que les seuls attributs que je veux convertir sont la frontière, la taille et la largeur. Tout le reste est sans importance et nous le faisons à la main de toute façon. – NateDSaint

+1

En outre, je devrais probablement commenter que je n'essaye pas de créer un moteur de rendu de CSS qui est entièrement conscient de quelles balises ont quels attributs de style et soyez conscient de la manière de les traiter. Dans le cas particulier où je suis fourni, l'outil qu'ils utilisent stipule uniquement la taille des cellules de table individuelles, tout le reste reçoit un identifiant ou une classe et est stylé avec une feuille externe qui est ignorée par le pdfmaker dans tous les cas. – NateDSaint

+0

La séparation était là où j'avais du mal à faire, en prenant soin de tous les frapper. En ce qui concerne l'utilisation des formats appropriés pour perl, j'essayais à l'origine de le faire aussi rapidement que possible pour la pratique, mais la diffusion en continu d'un fichier in et d'un fichier out s'avérait délicate, je l'ai donc fait rapidement. Merci de votre aide! – NateDSaint

3

Jetez un oeil sur CPAN pour les modules d'analyse HTML comme HTML::TreeBuilder, HTML::DOM ou même des modules XML comme XML::LibXML.

est rapide exemple ci-dessous en HTML :: TreeBuilder qui ajoute border = "1" attribut à une balise qui a un style attribut avec contenu frontière:

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; 

qui reproduira le HTML mais avec border = "1" ajouté juste à la balise span.

À l'unisson de ces modules, vous pouvez également regarder CSS et CSS::DOM pour vous aider à analyser le bit CSS.

+0

Merci pour l'entrée, et ça a l'air génial! Je verrai si nous pouvons obtenir cette configuration sur notre serveur, mais pour l'instant je dois obtenir une méthode de script rapide là-bas. Mais c'est une bien meilleure solution à long terme, merci! – NateDSaint

2

Je ne connais pas votre position sur les logiciels propriétaires, mais PrinceXML est le meilleur convertisseur HTML vers PDF disponible.

+1

Je n'ai aucun problème avec les logiciels propriétaires, mais mon patron le fait. Nous travaillons généralement avec autant d'open-source que possible. Pas nécessairement pour économiser de l'argent (puisque les coûts de main-d'œuvre de l'intégration finissent par en faire un lavage dans la plupart des cas) mais l'intégrer à notre installation Apache sur mesure. – NateDSaint

Questions connexes