Compare commits

...

26 Commits

Author SHA1 Message Date
Vicent Marti a7a2479f85 Release 2.2.1 2012-09-02 22:43:50 +02:00
Vicent Marti a8b230a490 Properly escape TeX data. 2012-09-02 22:27:04 +02:00
bootstraponline 041b01f171 Fix #490 2012-09-01 12:33:08 -06:00
Vicent Marti 1c475f3215 Release 2.2.0 2012-09-01 12:24:17 +02:00
Vicent Marti ab699d94b0 Do not render LaTeX locally
Offload the rendering service to MathTran.org, the free and open-source
LaTeX rendering engine.
2012-09-01 12:23:02 +02:00
bootstraponline 942d32c9b6 Fix uninstall command 2012-08-30 21:36:11 -06:00
bootstraponline 97f15f0b18 Add edit hotkey. #496 2012-08-30 21:07:57 -06:00
bootstraponline ed49358c50 Add h1-3 shortcuts. #496 2012-08-30 20:54:20 -06:00
bootstraponline 85eeecd140 Add save hotkey. 2012-08-30 20:48:44 -06:00
bootstraponline 30fd40fbe5 Fix #498
Ruby 1.8.7 and ri can't deal with <!-- appearing in documentation.
Please upgrade to the latest Ruby if you're able to.
2012-08-30 20:29:53 -06:00
bootstraponline 2ed262cacd v2.1.9 2012-08-30 20:12:28 -06:00
bootstraponline 29a1ef8f8a Skip doc on install 2012-08-30 20:04:20 -06:00
bootstraponline 772d18ee62 Fix uninstall command 2012-08-30 20:00:54 -06:00
bootstraponline dd604d9942 Fix #498 2012-08-30 19:57:20 -06:00
bootstraponline 85abc83427 v2.1.8 2012-08-30 19:41:54 -06:00
bootstraponline 6d8220629c Fix #500 2012-08-30 19:37:57 -06:00
bootstraponline 6ff7ada096 Fix #495 2012-08-30 18:31:35 -03:00
bootstraponline 8575049de5 Update get /data
Remove leading slash from page
Fix edge case with path set to '/'
2012-08-30 11:40:07 -03:00
bootstraponline bb6fb0c253 pathName is undefined when not found instead of 0 2012-08-30 11:18:02 -03:00
bootstraponline eb2ad9f840 Fix path
.key now returns undefined on failure so path can be 0.
If path is undefined then use an empty string.
2012-08-30 11:14:24 -03:00
bootstraponline d0dd23fc11 Fix path 2012-08-30 11:09:44 -03:00
bootstraponline 7ae4acbdb0 Fix #497 #492
Data url now includes path.
2012-08-30 11:03:11 -03:00
bootstraponline 881590ab37 Merge pull request #494 from releu/fix-requiring-uri-encoding-components
Move require "uri_encode_component"
2012-08-28 18:33:18 -07:00
Jan Bernacki 5e479dc5d9 move require 2012-08-28 22:44:09 +04:00
bootstraponline 7db9c2e762 Merge pull request #487 from jm/master
Fix bug with missing variable 'ext'
2012-08-27 11:08:19 -07:00
Jeremy McAnally 3767a11d21 Rename variables to be more clear and fix reference to non-existent 'ext' variable 2012-07-04 12:33:09 -04:00
13 changed files with 86 additions and 408 deletions
+7 -3
View File
@@ -497,9 +497,13 @@ like Rack::Auth, OmniAuth, etc.
Precious::App.set(:wiki_options, {:universal_toc => false}) Precious::App.set(:wiki_options, {:universal_toc => false})
run Precious::App run Precious::App
## Windows Filename Validation ## WINDOWS FILENAME VALIDATION
Note that filenames on windows must not contain any of the following characters `\ / : * ? " < > |`. See [this support article](http://support.microsoft.com/kb/177506) for details. Note that filenames on windows must not contain any of the following characters `\ / : * ? " < > |`. See [this support article](http://support.microsoft.com/kb/177506) for details.
## LIB.SO ERROR
`Could not open library 'lib.so'` may be solved by installing `python-devel` on Fedora or `python-dev` on Ubuntu.
## CONTRIBUTE ## CONTRIBUTE
If you'd like to hack on Gollum, start by forking my repo on GitHub: If you'd like to hack on Gollum, start by forking my repo on GitHub:
@@ -528,8 +532,8 @@ your changes merged back into core is as follows:
$ gem push gollum-X.Y.Z.gem $ gem push gollum-X.Y.Z.gem
## BUILDING THE GEM FROM MASTER ## BUILDING THE GEM FROM MASTER
$ gem uninstall -aix gollum $ gem uninstall -aIx gollum
$ git clone https://github.com/github/gollum.git $ git clone https://github.com/github/gollum.git
$ cd gollum $ cd gollum
gollum$ rake build gollum$ rake build
gollum$ gem install pkg/gollum*.gem gollum$ gem install --no-ri --no-rdoc pkg/gollum*.gem
+3 -3
View File
@@ -5,8 +5,8 @@ Gem::Specification.new do |s|
s.required_ruby_version = ">= 1.8.7" s.required_ruby_version = ">= 1.8.7"
s.name = 'gollum' s.name = 'gollum'
s.version = '2.1.7' s.version = '2.2.1'
s.date = '2012-08-25' s.date = '2012-09-02'
s.rubyforge_project = 'gollum' s.rubyforge_project = 'gollum'
s.summary = "A simple, Git-powered wiki." s.summary = "A simple, Git-powered wiki."
@@ -27,7 +27,7 @@ Gem::Specification.new do |s|
s.add_dependency('github-markup', [">= 0.7.0", "< 1.0.0"]) s.add_dependency('github-markup', [">= 0.7.0", "< 1.0.0"])
s.add_dependency('github-markdown') s.add_dependency('github-markdown')
s.add_dependency('pygments.rb', "~> 0.2.0") s.add_dependency('pygments.rb', "~> 0.2.0")
s.add_dependency('posix-spawn', "~> 0.3.0") s.add_dependency('escape_utils', "0.2.4")
s.add_dependency('sinatra', "~> 1.0") s.add_dependency('sinatra', "~> 1.0")
s.add_dependency('mustache', [">= 0.11.2", "< 1.0.0"]) s.add_dependency('mustache', [">= 0.11.2", "< 1.0.0"])
s.add_dependency('sanitize', "~> 2.0.0") s.add_dependency('sanitize', "~> 2.0.0")
+2 -1
View File
@@ -20,9 +20,10 @@ require File.expand_path('../gollum/markup', __FILE__)
require File.expand_path('../gollum/sanitization', __FILE__) require File.expand_path('../gollum/sanitization', __FILE__)
require File.expand_path('../gollum/tex', __FILE__) require File.expand_path('../gollum/tex', __FILE__)
require File.expand_path('../gollum/web_sequence_diagram', __FILE__) require File.expand_path('../gollum/web_sequence_diagram', __FILE__)
require File.expand_path('../gollum/frontend/uri_encode_component', __FILE__)
module Gollum module Gollum
VERSION = '2.1.7' VERSION = '2.2.1'
def self.assets_path def self.assets_path
::File.expand_path('gollum/frontend/public', ::File.dirname(__FILE__)) ::File.expand_path('gollum/frontend/public', ::File.dirname(__FILE__))
+7 -3
View File
@@ -100,9 +100,13 @@ module Gollum
tree.blobs.each do |blob| tree.blobs.each do |blob|
next if page_path_scheduled_for_deletion?(index.tree, fullpath) next if page_path_scheduled_for_deletion?(index.tree, fullpath)
file = blob.name.downcase.sub(/\.\w+$/, '')
file_ext = ::File.extname(blob.name).sub(/^\./, '') existing_file = blob.name.downcase.sub(/\.\w+$/, '')
if downpath == file && !(allow_same_ext && file_ext == ext) existing_file_ext = ::File.extname(blob.name).sub(/^\./, '')
new_file_ext = ::File.extname(path).sub(/^\./, '')
if downpath == existing_file && !(allow_same_ext && new_file_ext == existing_file_ext)
raise DuplicatePageError.new(dir, blob.name, path) raise DuplicatePageError.new(dir, blob.name, path)
end end
end end
-7
View File
@@ -9,7 +9,6 @@ require 'gollum/frontend/views/layout'
require 'gollum/frontend/views/editable' require 'gollum/frontend/views/editable'
require 'gollum/frontend/views/has_page' require 'gollum/frontend/views/has_page'
require File.expand_path '../uri_encode_component', __FILE__
require File.expand_path '../helpers', __FILE__ require File.expand_path '../helpers', __FILE__
# Fix to_url # Fix to_url
@@ -283,12 +282,6 @@ module Precious
mustache :compare mustache :compare
end end
get '/_tex.png' do
content_type 'image/png'
formula = Base64.decode64(params[:data])
Gollum::Tex.render_formula(formula)
end
get %r{^/(javascript|css|images)} do get %r{^/(javascript|css|images)} do
halt 404 halt 404
end end
@@ -196,7 +196,9 @@
LanguageDefinition.getHookFunctionFor("activate")(); LanguageDefinition.getHookFunctionFor("activate")();
} }
function hotkey( cmd ) { function hotkey( e, cmd ) {
e.preventDefault();
var def = LanguageDefinition.getDefinitionFor( cmd ); var def = LanguageDefinition.getDefinitionFor( cmd );
if ( typeof def == 'object' ) { if ( typeof def == 'object' ) {
FunctionBar.executeAction( def ); FunctionBar.executeAction( def );
@@ -205,8 +207,17 @@
return false; return false;
} }
Mousetrap.bind(['command+b', 'ctrl+b'], function(){ hotkey('function-bold'); }); Mousetrap.bind(['command+1', 'ctrl+1'], function( e ){ hotkey( e, 'function-h1' ); });
Mousetrap.bind(['command+i', 'ctrl+i'], function(){ hotkey('function-italic'); }); Mousetrap.bind(['command+2', 'ctrl+2'], function( e ){ hotkey( e, 'function-h2' ); });
Mousetrap.bind(['command+3', 'ctrl+3'], function( e ){ hotkey( e, 'function-h3' ); });
Mousetrap.bind(['command+b', 'ctrl+b'], function( e ){ hotkey( e, 'function-bold' ); });
Mousetrap.bind(['command+i', 'ctrl+i'], function( e ){ hotkey( e, 'function-italic' ); });
Mousetrap.bind(['command+s', 'ctrl+s'], function( e ){
e.preventDefault();
$("#gollum-editor-submit").trigger("click");
return false;
});
} ); } );
} else { } else {
LanguageDefinition._ACTIVE_LANG = name; LanguageDefinition._ACTIVE_LANG = name;
@@ -16,7 +16,7 @@
<a id='toggle' class='edit' href='javascript:void(0)' onclick='jsm.toggleLeftRight();'><img src='images/lr_24.png' alt='Toggle left to right' title='Toggle left to right'></a> <a id='toggle' class='edit' href='javascript:void(0)' onclick='jsm.toggleLeftRight();'><img src='images/lr_24.png' alt='Toggle left to right' title='Toggle left to right'></a>
</div> </div>
<div class='editor_bg'></div> <div id='editor_bg' class='editor_bg'></div>
<div class='toolpanel_bg'></div> <div class='toolpanel_bg'></div>
<div id='commenttoolpanel' class='toolpanel edit' style='width: 500px; right: 0px; '> <div id='commenttoolpanel' class='toolpanel edit' style='width: 500px; right: 0px; '>
@@ -62,9 +62,10 @@ initAce( commentEditor, commentEditorSession );
var baseUrl = location.pathname.split('/').slice(0,-2).join('/'); var baseUrl = location.pathname.split('/').slice(0,-2).join('/');
// RegExp from http://stackoverflow.com/questions/901115/get-query-string-values-in-javascript // RegExp from http://stackoverflow.com/questions/901115/get-query-string-values-in-javascript
// Returns value on success and undefined on failure.
$.key = function( key ) { $.key = function( key ) {
var value = new RegExp( '[\\?&]' + key + '=([^&#]*)' ).exec( location.href ); var value = new RegExp( '[\\?&]' + key + '=([^&#]*)' ).exec( location.href );
return ( !value ) ? 0 : value[ 1 ] || 0; return ( !value ) ? undefined : value[ 1 ] || undefined;
} }
// True if &create=true // True if &create=true
@@ -73,10 +74,6 @@ var create = $.key( 'create' );
var pageName = $.key( 'page' ); var pageName = $.key( 'page' );
var pathName = $.key( 'path' ); var pathName = $.key( 'path' );
if ( pathName === 0 ) {
pathName = undefined;
}
defaultCommitMessage = function() { defaultCommitMessage = function() {
var msg = pageName + ' (markdown)'; var msg = pageName + ' (markdown)';
@@ -100,7 +97,7 @@ $.save = function( commitMessage ) {
var newLocation = baseUrl; var newLocation = baseUrl;
function clean( str ) { function clean( str ) {
return str.replace(/^\/+/g, '/'); return str.replace(/^\/+/, '/');
} }
// 'a%2Fb' => a/b // 'a%2Fb' => a/b
@@ -341,10 +338,23 @@ var applyTimeout = function () {
/* Load markdown from /data/page into the ace editor. /* Load markdown from /data/page into the ace editor.
~-1 == false; !~-1 == true; ~-1 == false; !~-1 == true;
*/ */
if ( !~location.host.indexOf('github.com') ) { if ( !~ location.host.indexOf( 'github.com' ) ) {
// returns unescaped key with leading slashes removed
function key_no_leading_slash( key ) {
return unescape( $.key( key ) || '' ).replace( /^\/+/, '' );
}
// ensure leading / is removed from path and that it ends with /
var path = key_no_leading_slash( 'path' );
// don't append '/' if path is empty from removing leading slash
if ( path !== '' && path.charAt( path.length - 1 ) !== '/' ) {
path += '/';
}
jQuery.ajax( { jQuery.ajax( {
type: 'GET', type: 'GET',
url: baseUrl + '/data/' + $.key( 'page' ), url: baseUrl + '/data/' + path + key_no_leading_slash( 'page' ),
success: function( data ) { success: function( data ) {
editorSession.setValue( data ); editorSession.setValue( data );
} }
@@ -455,6 +465,11 @@ var applyTimeout = function () {
win.jsm.resize = resize; win.jsm.resize = resize;
// remove editor_bg after loading because
// it'll cause problems if toggle left right is used
var ebg = doc.getElementById('editor_bg');
ebg.parentNode.removeChild(ebg);
/* /*
Resize can be called an absurd amount of times Resize can be called an absurd amount of times
and will crash the page without debouncing. and will crash the page without debouncing.
@@ -1,3 +1,10 @@
<script>
Mousetrap.bind(['e'], function( e ) {
e.preventDefault();
window.location = "/edit" + window.location.pathname;
return false;
});
</script>
<div id="wiki-wrapper" class="page"> <div id="wiki-wrapper" class="page">
<div id="head"> <div id="head">
<h1>{{title}}</h1> <h1>{{title}}</h1>
+5
View File
@@ -6,6 +6,11 @@ module Precious
attr_reader :page, :content attr_reader :page, :content
# return path set in app.rb not @page.path
def path
@path
end
def title def title
"#{@page.title}" "#{@page.title}"
end end
+8 -18
View File
@@ -155,23 +155,7 @@ module Gollum
def process_tex(data) def process_tex(data)
@texmap.each do |id, spec| @texmap.each do |id, spec|
type, tex = *spec type, tex = *spec
data.gsub!(id, Gollum::Tex.to_html(tex, type))
# Obtain the formula with parameters
out = nil
begin
width, height, align, base64 = Gollum::Tex.render_formula(tex, true)
# TODO: Should we load the binary inside the html?
#out = %{<img width="#{width}" height="#{height}" style="vertical-align: #{align}px;" src="data:image/png;base64,\n#{base64}" alt="#{CGI.escapeHTML(tex)}" />}
# Use the alignment values from the formula rendering but still use the call to '_tex.png'. Although it will call render_formula()
# again, it will use the already cached formula and it might have some advantages from the point of view of browser caching (really not sure here).
out = %{<img width="#{width}" height="#{height}" style="vertical-align: #{align}px;" src="#{::File.join(@wiki.base_path, '_tex.png')}?type=#{type}&data=#{Base64.encode64(tex).chomp}" alt="#{CGI.escapeHTML(tex)}" />}
rescue # In case of error
out = CGI.escapeHTML(tex)
end
data.gsub!(id, out)
end end
data data
end end
@@ -591,12 +575,18 @@ module Gollum
######################################################################### #########################################################################
# Extract metadata for data and build metadata table. Metadata # Extract metadata for data and build metadata table. Metadata
# is content found between `<!-- ---` and `-->` markers, and must # is content found between markers, and must
# be a valid YAML mapping. # be a valid YAML mapping.
# #
# Because ri and ruby 1.8.7 are awesome, the markers can't
# be included in this documentation without triggering
# `Unhandled special: Special: type=17`
# Please read the source code for the exact markers
#
# Returns the String of formatted data with metadata removed. # Returns the String of formatted data with metadata removed.
def extract_metadata(data) def extract_metadata(data)
@metadata ||= {} @metadata ||= {}
# The markers are `<!-- ---` and `-->`
data.gsub(/\<\!--+\s+---(.*?)--+\>/m) do data.gsub(/\<\!--+\s+---(.*?)--+\>/m) do
yaml = @wiki.sanitizer.clean($1) yaml = @wiki.sanitizer.clean($1)
hash = YAML.load(yaml) hash = YAML.load(yaml)
+7 -359
View File
@@ -1,366 +1,14 @@
require 'fileutils' require 'escape_utils'
require 'shellwords'
require 'tmpdir'
require 'posix/spawn'
require 'base64'
module Gollum module Gollum
module Tex module Tex
class Error < StandardError; end TEX_URL = "http://www.mathtran.org/cgi-bin/toy/"
TEX_SIZES = { :inline => 2, :block => 4 }
extend POSIX::Spawn def self.to_html(tex, type = :inline)
tex_uri = EscapeUtils.escape_uri(tex)
Template = <<-EOS tex_alt = EscapeUtils.escape_html(tex)
\\documentclass[11pt]{article} %{<img src="#{TEX_URL}?D=#{TEX_SIZES[type]};tex=#{tex_uri}" alt="#{tex_alt}">}
\\pagestyle{empty}
\\setlength{\\topskip}{0pt}
\\setlength{\\parindent}{0pt}
\\setlength{\\abovedisplayskip}{0pt}
\\setlength{\\belowdisplayskip}{0pt}
\\usepackage{geometry}
\\usepackage{amsfonts}
\\usepackage{amsmath}
\\newsavebox{\\snippetbox}
\\newlength{\\snippetwidth}
\\newlength{\\snippetheight}
\\newlength{\\snippetdepth}
\\newlength{\\pagewidth}
\\newlength{\\pageheight}
\\newlength{\\pagemargin}
\\begin{lrbox}{\\snippetbox}%
\$%s\$
\\end{lrbox}
\\settowidth{\\snippetwidth}{\\usebox{\\snippetbox}}
\\settoheight{\\snippetheight}{\\usebox{\\snippetbox}}
\\settodepth{\\snippetdepth}{\\usebox{\\snippetbox}}
\\setlength\\pagemargin{4pt}
\\setlength\\pagewidth\\snippetwidth
\\addtolength\\pagewidth\\pagemargin
\\addtolength\\pagewidth\\pagemargin
\\setlength\\pageheight\\snippetheight
\\addtolength{\\pageheight}{\\snippetdepth}
\\addtolength\\pageheight\\pagemargin
\\addtolength\\pageheight\\pagemargin
\\newwrite\\foo
\\immediate\\openout\\foo=\\jobname.dimensions
\\immediate\\write\\foo{snippetdepth = \\the\\snippetdepth}
\\immediate\\write\\foo{snippetheight = \\the\\snippetheight}
\\immediate\\write\\foo{snippetwidth = \\the\\snippetwidth}
\\immediate\\write\\foo{pagewidth = \\the\\pagewidth}
\\immediate\\write\\foo{pageheight = \\the\\pageheight}
\\immediate\\write\\foo{pagemargin = \\the\\pagemargin}
\\closeout\\foo
\\geometry{paperwidth=\\pagewidth,paperheight=\\pageheight,margin=\\pagemargin}
\\begin{document}%
\\usebox{\\snippetbox}%
\\end{document}
EOS
class << self
attr_accessor :latex_path
end end
self.latex_path = 'pdflatex'
def self.check_dependencies!
return if @dependencies_available
if `which pdflatex` == ""
raise Error, "`pdflatex` command not found"
end
if `which gs` == ""
raise Error, "`gs` command not found"
end
if `which pnmcrop` == ""
raise Error, "`pnmcrop` command not found"
end
if `which pnmpad` == ""
raise Error, "`pnmpad` command not found"
end
if `which pnmscale` == ""
raise Error, "`pnmscale` command not found"
end
if `which ppmtopgm` == ""
raise Error, "`ppmtopgm` command not found"
end
if `which pnmgamma` == ""
raise Error, "`pnmgamma` command not found"
end
if `which pnmtopng` == ""
raise Error, "`pnmtopng` command not found"
end
@dependencies_available = true
end
# Render the formula and calculate the correct alignment
# for the image in the html.
#
# This is a ruby implementation of the Perl version described
# at http://tex.stackexchange.com/questions/44486/pixel-perfect-vertical-alignment-of-image-rendered-tex-snippets
#
# The main caveat is that rendering takes quite a bit of processing power,
# which can make the page load slowly if it has to render each time.
# For this reason, the method caches the rendered formula in `/tmp` for reduced
# loading time in subsequent loads.
#
# @param formula the tex formula to render
# @param with_properties, if true it returns an array with a base64
# string with the image, and the alignment values for the image.
# Otherwise it returns the binary image.
def self.render_formula(formula, with_properties=false)
check_dependencies!
render_antialias_bits = 4
render_oversample = 4
display_oversample = 4
gamma = 0.3
if !with_properties
display_oversample = 1
gamma = 0.5
end
oversample = render_oversample * display_oversample
render_dpi = 96*1.2 * 72.27/72 * oversample # This is 1850.112 dpi.
# Cache rendered formula and returned cached version if it exists
# First look for the .cache directory in the home folder
cache_dir = ::File.expand_path("~/.cache")
if not ::File.exists?(cache_dir) or not ::File.directory?(cache_dir)
::Dir.mkdir(cache_dir)
end
# Check that the gollum directory exists inside the cache dir
cache_dir = ::File.join(cache_dir, "gollum")
if not ::File.exists?(cache_dir) or not ::File.directory?(cache_dir)
::Dir.mkdir(cache_dir)
end
# Check for the formula in the cache dir
hash = Digest::SHA1.hexdigest(formula)
cache_file = ::File.join(cache_dir, "tex-#{hash}")
if ::File.exists?(cache_file)
width, height, align, base64 = ::File.open(cache_file, 'rb') { |io| io.read }.split(",")
if with_properties
return width, height, align, base64
else
return Base64.decode64(base64)
end
end
Dir.mktmpdir('tex') do |path|
file = ::File.join(path, "formula")
# --- Write TeX source and compile to PDF.Write snippet into template
::File.open(file + ".tex", 'w') { |f| f.write(Template % formula) }
result = sh_chdir path, "pdflatex",
"-halt-on-error",
"-output-directory=#{path}",
"-output-format=pdf",
"#{file}.tex",
">#{file}.err 2>&1"
# --- Convert PDF to PNM using Ghostscript.
sh "gs",
"-q -dNOPAUSE -dBATCH",
"-dTextAlphaBits=#{render_antialias_bits}",
"-dGraphicsAlphaBits=#{render_antialias_bits}",
"-r#{render_dpi}",
"-sDEVICE=pnmraw",
"-sOutputFile=#{file}.pnm",
"#{file}.pdf"
img_width, img_height = pnm_width_height(file + ".pnm")
# --- Read dimensions file written by TeX during processing.
#
# Example of file contents:
# snippetdepth = 6.50009pt
# snippetheight = 13.53899pt
# snippetwidth = 145.4777pt
# pagewidth = 153.4777pt
# pageheight = 28.03908pt
# pagemargin = 4.0pt
dimensions = {}
::File.open(file + ".dimensions").readlines.each_with_index do |line, i|
if line =~ /^(\S+)\s+=\s+(-?[0-9\.]+)pt$/
dimensions[$1] = Float($2) / 72.27 * render_dpi
else
raise Error, "#{file}.dimensions: invalid line: #{i}"
end
end
# --- Crop bottom, then measure how much was cropped.
sh "pnmcrop -white -bottom #{file}.pnm >#{file}.bottomcrop.pnm"
#raise Error, "`pnmcrop` command failed: #{result}" unless ::File.exist?(file + ".bottomcrop.pnm")
img_width_bottomcrop, img_height_bottomcrop = pnm_width_height("#{file}.bottomcrop.pnm")
bottomcrop = img_height - img_height_bottomcrop
# --- Crop top and sides, then measure how much was cropped from the top.
sh "pnmcrop -white #{file}.bottomcrop.pnm > #{file}.crop.pnm"
#raise Error, "`pnmcrop` command failed: #{result}" unless ::File.exist?(file + ".crop.pnm")
cropped_img_width, cropped_img_height = pnm_width_height("#{file}.crop.pnm")
topcrop = img_height_bottomcrop - cropped_img_height
# --- Pad image with specific values on all four sides, in preparation for
# downsampling.
# Calculate bottom padding.
snippet_depth = Integer(dimensions["snippetdepth"] + dimensions["pagemargin"] + 0.5) - bottomcrop
padded_snippet_depth = round_up(snippet_depth, oversample)
increase_snippet_depth = padded_snippet_depth - snippet_depth
bottom_padding = increase_snippet_depth
# --- Next calculate top padding, which depends on bottom padding.
padded_img_height = round_up(cropped_img_height + bottom_padding,
oversample)
top_padding = padded_img_height - (cropped_img_height + bottom_padding)
# --- Calculate left and right side padding. Distribute padding evenly.
padded_img_width = round_up(cropped_img_width, oversample)
left_padding = Integer((padded_img_width - cropped_img_width) / 2.0)
right_padding = (padded_img_width - cropped_img_width) - left_padding
# --- Pad the final image.
result = sh "pnmpad",
"-white",
"-bottom=#{bottom_padding}",
"-top=#{top_padding}",
"-left=#{left_padding}",
"-right=#{right_padding}",
"#{file}.crop.pnm",
">#{file}.pad.pnm"
# --- Sanity check of final size.
final_pnm_width, final_pnm_height = pnm_width_height(file + ".pad.pnm")
raise Error, "#{final_pnm_width} is not a multiple of #{oversample}" unless final_pnm_width % oversample == 0
raise "#{final_pnm_height} is not a multiple of #{oversample}" unless final_pnm_height % oversample == 0
# --- Convert PNM to PNG.
final_png_width = final_pnm_width / render_oversample
final_png_height = final_pnm_height / render_oversample
result = sh "cat #{file}.pad.pnm",
"| ppmtopgm",
"| pnmscale -reduce #{render_oversample}",
"| pnmgamma #{gamma}",
"| pnmtopng -compression 9",
"> #{file}.png"
raise Error, "Conversion to png failed: #{result}" unless ::File.exist?(file + ".png")
# Calculate html properties
html_img_width = final_png_width / display_oversample
html_img_height = final_png_height / display_oversample
html_img_vertical_align = sprintf("%.0f", -padded_snippet_depth / oversample)
png_data_base64 = Base64.encode64(::File.open("#{file}.png") { |io| io.read }).chomp
::File.open(cache_file, 'w') { |f| f.write(%{#{html_img_width},#{html_img_height},#{html_img_vertical_align},#{png_data_base64}}) }
if with_properties
return html_img_width, html_img_height, html_img_vertical_align, png_data_base64
else
::File.read(file + ".png")
end
end
end
private
def self.sh_chdir(path, *args)
origcommand = args * " "
return if origcommand == ""
command = origcommand
command.gsub! /(["\\])/, "\\$1"
command = %{/bin/sh -c "(#{command}) 2>&1"}
pid = spawn command, :chdir => path
result = Process::waitpid(pid)
exit_value = Integer($? >> 8), signal_num = Integer($? & 127), dumped_core = Integer($? & 128)
raise Error, "Failed #{result}: #{origcommand}. Exit value = #{exit_value}. Signal Num = #{signal_num}. Dumped core = #{dumped_core}" unless $?.success?
return result
end
def self.sh(*args)
origcommand = args * " "
return if origcommand == ""
command = origcommand
command.gsub! /(["\\])/, "\\$1"
command = %{/bin/sh -c "(#{command}) 2>&1"}
pid = spawn command
#pid = spawn *args
result = Process::waitpid(pid)
exit_value = $? >> 8, signal_num = $? & 127, dumped_core = $? & 128
raise Error, "Failed #{result}: #{origcommand}. Exit value = #{exit_value}. Signal Num = #{signal_num}. Dumped core = #{dumped_core}" unless $?.success?
return result
end
def self.round_up(num, mod)
num + (num % mod == 0 ? 0 : (mod - (num % mod)))
end
def self.pnm_width_height(filename)
raise Error, "#{filename} is not a .pnm file" if filename !~ /\.pnm$/
width = nil, height = nil
::File.open(filename) do |file|
# Read first line
line = file.gets
begin
line = file.gets # Read next line, skipping comments
end while line && line =~ /^#/
if line =~ /^(\d+)\s+(\d+)$/
width = Integer($1)
height = Integer($2)
else
raise Error, "#{filename}: couldn't read image size"
end
end
raise Error, "#{filename}: couldn't read image size" unless width && height
return width, height
end
end end
end end
+2 -2
View File
@@ -670,13 +670,13 @@ end
test "TeX block syntax" do test "TeX block syntax" do
content = 'a \[ a^2 \] b' content = 'a \[ a^2 \] b'
output = "<p>a<imgwidth=\"15\"height=\"16\"style=\"vertical-align:-1px;\"src=\"/_tex.png?type=block&data=YV4y\"alt=\"a^2\"/>b</p>" output = "<p>a<imgsrc=\"http://www.mathtran.org/cgi-bin/toy/?D=4;tex=a^2\"alt=\"a^2\">b</p>"
compare(content, output, 'md') compare(content, output, 'md')
end end
test "TeX inline syntax" do test "TeX inline syntax" do
content = 'a \( a^2 \) b' content = 'a \( a^2 \) b'
output = "<p>a<imgwidth=\"15\"height=\"16\"style=\"vertical-align:-1px;\"src=\"/_tex.png?type=inline&data=YV4y\"alt=\"a^2\"/>b</p>" output = "<p>a<imgsrc=\"http://www.mathtran.org/cgi-bin/toy/?D=2;tex=a^2\"alt=\"a^2\">b</p>"
compare(content, output, 'md') compare(content, output, 'md')
end end