package Org::To::HTML; { $Org::To::HTML::VERSION = '0.07'; } # ABSTRACT: Export Org document to HTML use 5.010; use Log::Any '$log'; use vars qw($VERSION); use File::Slurp; use HTML::Entities qw/encode_entities/; use Org::Document; use Moo; with 'Org::To::Role'; extends 'Org::To::Base'; require Exporter; our @ISA; push @ISA, qw(Exporter); our @EXPORT_OK = qw(org_to_html); has naked => (is => 'rw'); has html_title => (is => 'rw'); has css_url => (is => 'rw'); our %SPEC; $SPEC{org_to_html} = { summary => 'Export Org document to HTML', description => <<'_', This is the non-OO interface. For more customization, consider subclassing Org::To::HTML. _ args => { source_file => ['str' => { summary => 'Source Org file to export', }], source_str => ['str' => { summary => 'Alternatively you can specify Org string directly', }], target_file => ['str' => { summary => 'HTML file to write to', description => <<'_', If not specified, HTML string will be returned. _ }], include_tags => ['array' => { of => 'str*', summary => 'Include trees that carry one of these tags', description => <<'_', Works like Org's 'org-export-select-tags' variable. If the whole document doesn't have any of these tags, then the whole document will be exported. Otherwise, trees that do not carry one of these tags will be excluded. If a selected tree is a subtree, the heading hierarchy above it will also be selected for export, but not the text below those headings. _ }], exclude_tags => ['array' => { of => 'str*', summary => 'Exclude trees that carry one of these tags', description => <<'_', If the whole document doesn't have any of these tags, then the whole document will be exported. Otherwise, trees that do not carry one of these tags will be excluded. If a selected tree is a subtree, the heading hierarchy above it will also be selected for export, but not the text below those headings. exclude_tags is evaluated after include_tags. _ }], html_title => ['str' => { summary => 'HTML document title, defaults to source_file', }], css_url => ['str' => { summary => 'Add a link to CSS document', }], naked => ['bool' => { summary => 'Don\'t wrap exported HTML with HTML/HEAD/BODY elements', }], } }; sub org_to_html { my %args = @_; my $doc; if ($args{source_file}) { $doc = Org::Document->new(from_string => scalar read_file($args{source_file})); } elsif (defined($args{source_str})) { $doc = Org::Document->new(from_string => $args{source_str}); } else { return [400, "Please specify source_file/source_str"]; } my $obj = __PACKAGE__->new( include_tags => $args{include_tags}, exclude_tags => $args{exclude_tags}, css_url => $args{css_url}, naked => $args{naked}, html_title => $args{html_title} // $args{source_file}, ); my $html = $obj->export($doc); #$log->tracef("html = %s", $html); if ($args{target_file}) { write_file($args{target_file}, $html); return [200, "OK"]; } else { return [200, "OK", $html]; } } sub export_document { my ($self, $doc) = @_; my $html = []; unless ($self->naked) { push @$html, "\n"; push @$html, ( "\n\n"); push @$html, "
\n"; push @$html, "
join "", (
"name), "\">",
encode_entities($elem->raw_content),
"\n\n"
);
}
sub export_fixed_width_section {
my ($self, $elem) = @_;
join "", (
"",
encode_entities($elem->text),
"\n"
);
}
sub export_comment {
my ($self, $elem) = @_;
join "", (
"\n"
);
}
sub export_drawer {
my ($self, $elem) = @_;
# currently not exported
'';
}
sub export_footnote {
my ($self, $elem) = @_;
# currently not exported
'';
}
sub export_headline {
my ($self, $elem) = @_;
my @htags = $elem->get_tags;
my @children = @{$elem->children // []};
if ($self->include_tags) {
if (!defined(first {$_ ~~ @htags} @{$self->include_tags})) {
# headline doesn't contain include_tags, select only
# suheadlines that contain them
@children = ();
for my $c (@{ $elem->children // []}) {
next unless $c->isa('Org::Element::Headline');
my @hl_included = $elem->find(
sub {
my $el = shift;
return unless
$elem->isa('Org::Element::Headline');
my @t = $elem->get_tags;
return defined(first {$_ ~~ @t}
@{$self->include_tags});
});
next unless @hl_included;
push @children, $c;
}
return '' unless @children;
}
}
if ($self->exclude_tags) {
return '' if defined(first {$_ ~~ @htags}
@{$self->exclude_tags});
}
join "", (
"level, ">",
$self->export_elements($elem->title),
" level, ">\n\n",
$self->export_elements(@children)
);
}
sub export_list {
my ($self, $elem) = @_;
my $tag;
my $type = $elem->type;
if ($type eq 'D') { $tag = 'DL' }
elsif ($type eq 'O') { $tag = 'OL' }
elsif ($type eq 'U') { $tag = 'UL' }
join "", (
"<$tag>\n",
$self->export_elements(@{$elem->children // []}),
"$tag>\n\n"
);
}
sub export_list_item {
my ($self, $elem) = @_;
my $html = [];
if ($elem->desc_term) {
push @$html, "\n\n/g;
push @$html, $text;
push @$html, $self->export_elements(@{$elem->children}) if $elem->children;
push @$html, "$tag>" if $tag;
join "", @$html;
}
sub export_time_range {
my ($self, $elem) = @_;
$elem->as_string;
}
sub export_timestamp {
my ($self, $elem) = @_;
$elem->as_string;
}
sub export_link {
my ($self, $elem) = @_;
my $html = [];
push @$html, "link =~ m!^\w+:!) {
# looks like a url
push @$html, $elem->link;
} else {
# assume it's an anchor
push @$html, "#", __escape_target($elem->link);
}
push @$html, "\">";
if ($elem->description) {
push @$html, $self->export_elements($elem->description);
} else {
push @$html, $elem->link;
}
push @$html, "";
join "", @$html;
}
1;
=pod
=head1 NAME
Org::To::HTML - Export Org document to HTML
=head1 VERSION
version 0.07
=head1 SYNOPSIS
use Org::To::HTML qw(org_to_html);
# non-OO interface
my $res = org_to_html(
source_file => 'todo.org', # or source_str
#target_file => 'todo.html', # defaults return the HTML in $res->[2]
#html_title => 'My Todo List', # defaults to file name
#include_tags => [...], # default exports all tags.
#exclude_tags => [...], # behavior mimics emacs's include/exclude rule
#css_url => '/path/to/my/style.css', # default none
#naked => 0, # if set to 1, no HTML/HEAD/BODY will be output.
);
die "Failed" unless $res->[0] == 200;
# OO interface
my $oeh = Org::To::HTML->new();
my $html = $oeh->export($doc); # $doc is Org::Document object
=head1 DESCRIPTION
Export Org format to HTML. To customize, you can subclass this module.
This module uses L