aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dumb_render.f9093
-rw-r--r--files.f90162
-rw-r--r--gemini.prj82
-rw-r--r--layout.f9051
-rw-r--r--render.f90173
-rw-r--r--samples/sample1.gmi14
-rw-r--r--test.f9060
7 files changed, 635 insertions, 0 deletions
diff --git a/dumb_render.f90 b/dumb_render.f90
new file mode 100644
index 0000000..f50efe1
--- /dev/null
+++ b/dumb_render.f90
@@ -0,0 +1,93 @@
+module dumb_render
+use render
+implicit none
+
+ type, extends(renderer) :: dumb_renderer
+
+ contains
+
+ procedure :: initialize => dumb_initialize
+
+ procedure :: text_width => dumb_text_width
+ procedure :: text_height => dumb_text_height
+ procedure :: is_text_visible => dumb_text_visible
+
+ procedure :: draw_porportional => dumb_text_draw
+
+ procedure :: preformatted_width => dumb_text_width
+ procedure :: preformatted_height => dumb_text_height
+ procedure :: is_preformatted_visible => dumb_text_visible
+
+ procedure :: draw_preformatted => dumb_text_draw
+
+ end type dumb_renderer
+
+contains
+
+ subroutine dumb_initialize(self)
+ implicit none
+
+ class(dumb_renderer)::self
+
+ self%max_width = 79
+ self%y = 0
+
+ end subroutine dumb_initialize
+
+ function dumb_text_width(self, text)
+ implicit none
+
+ class(dumb_renderer)::self
+ character(*), intent(in)::text
+ integer::dumb_text_width
+
+ dumb_text_width = len_trim(text)
+
+ end function dumb_text_width
+
+ function dumb_text_height(self, text)
+ implicit none
+
+ class(dumb_renderer)::self
+ character(*), intent(in)::text
+ integer::dumb_text_height
+
+ dumb_text_height = 1
+
+ end function dumb_text_height
+
+ function dumb_text_visible(self, text)
+ implicit none
+
+ class(dumb_renderer)::self
+ character(*), intent(in)::text
+ logical::dumb_text_visible
+
+ dumb_text_visible = (self%y >= 0 .AND. self%y < 24)
+
+ end function dumb_text_visible
+
+ subroutine dumb_text_draw(self, text)
+ implicit none
+
+ class(dumb_renderer)::self
+ character(*), intent(in)::text
+ integer::limit_x
+ character(5)::formatting
+
+ limit_x = min(len_trim(text), self%max_width)
+ if(limit_x == 0) then
+ write(*,*) " "
+ else
+ if(limit_x < 10) then
+ write(formatting, '(A2, I1, A1)') '(A', limit_x, ')'
+ else
+ write(formatting, '(A2, I2, A1)') '(A', limit_x, ')'
+ end if
+
+ write(*, formatting) text(1:limit_x)
+ end if
+
+ end subroutine dumb_text_draw
+
+end module dumb_render \ No newline at end of file
diff --git a/files.f90 b/files.f90
new file mode 100644
index 0000000..45bc116
--- /dev/null
+++ b/files.f90
@@ -0,0 +1,162 @@
+module file_handling
+
+ integer, parameter::file_type_plain_text = 1
+ integer, parameter::file_type_gemini = 2
+
+contains
+
+ function read_line_text(unit_number, iostatus) result(res)
+ implicit none
+
+ character(len=:), allocatable::res
+ integer, intent(in)::unit_number
+ integer, intent(out)::iostatus
+ integer::startpos, endpos, length, i
+ character::c
+
+ inquire(unit=unit_number, pos=startpos)
+
+ endpos = startpos
+ read(unit_number, '(A1)', advance='no', iostat=iostatus) c
+ do while(c /= CHAR(10) .AND. iostatus == 0)
+ endpos = endpos + 1
+ read(unit_number, '(A1)', advance='no', iostat=iostatus) c
+ end do
+
+ length = (endpos - startpos + 1)
+ print '(A10, I8)', "allocated", length
+
+ allocate(character(len=length) :: res)
+ res = repeat(' ', length)
+
+ read(unit_number, '(A1)', pos=startpos, advance='no', iostat=iostatus) c
+ if(iostatus == 0) then
+ res(1:1) = c
+ do i=2, length
+ read(unit_number, '(A1)', advance='no', iostat=iostatus) c
+
+ if(iostatus /= 0) then
+ exit
+ end if
+
+ res(i:i) = c
+ end do
+ end if
+
+ end function read_line_text
+
+ subroutine process_line(single_line, file_type, preformatted_on)
+ use layout
+ implicit none
+
+ type(line), intent(inout)::single_line
+ integer, intent(in)::file_type
+ logical, intent(inout)::preformatted_on
+
+ integer::line_length
+
+ if(file_type == file_type_plain_text) then
+
+ single_line%line_type = line_type_preformatted
+ preformatted_on = .TRUE.
+
+ else if(file_type == file_type_gemini) then
+
+ line_length = len_trim(single_line%text)
+
+ if(line_length > 2 .AND. single_line%text(1:2) == "=>") then
+
+ single_line%line_type = line_type_link
+ single_line%text(1:2) = " "
+ single_line%text = adjustl(single_line%text)
+
+ else if(line_length >= 3 .AND. single_line%text(1:3) == "```") then
+
+ preformatted_on = .not. preformatted_on
+ single_line%line_type = line_type_indicator
+
+ else if(preformatted_on) then
+
+ single_line%line_type = line_type_preformatted
+
+ else
+
+ single_line%line_type = line_type_text
+
+ end if
+
+ end if
+
+ end subroutine process_line
+
+ function load_unit(unit_number, file_type) result(first_line)
+ use layout
+ implicit none
+
+ integer, intent(in)::unit_number, file_type
+ type(line), pointer::first_line
+
+ type(line), pointer::walker, next_line
+ character::c
+ integer::iostatus
+ logical::preformatted_on
+
+ preformatted_on = .FALSE.
+
+ allocate(first_line)
+
+ first_line%text = read_line_text(unit_number, iostatus)
+ first_line%next => null()
+
+ walker=>first_line
+
+ call process_line(walker, file_type, preformatted_on)
+
+ do while(iostatus /= -1) ! -1 should be end of file
+
+ allocate(next_line)
+ next_line%next => null()
+
+ walker%next => next_line
+ next_line => null()
+ walker => walker%next
+
+ walker%text = read_line_text(unit_number, iostatus)
+ print *, walker%text
+ call process_line(walker, file_type, preformatted_on)
+
+ end do
+
+ end function load_unit
+
+ function load_filename(filename, iostatus, mimetype) result(first_line)
+ use layout, only: line
+ implicit none
+
+ character(*), intent(in)::filename
+ integer, intent(out)::iostatus
+ character(*), intent(in), optional::mimetype
+
+ type(line), pointer::first_line
+
+ integer::filetype
+
+ open(unit=1097, file=filename, access='stream', iostat=iostatus, form='FORMATTED')
+ if(iostatus == 0) then
+
+ if(present(mimetype)) then
+ if(trim(mimetype) == 'text/gemini') then
+ filetype = file_type_gemini
+ else
+ filetype = file_type_plain_text
+ end if
+ else
+ filetype = file_type_plain_text
+ end if
+
+ first_line => load_unit(1097, filetype)
+ end if
+
+ end function load_filename
+
+end module file_handling \ No newline at end of file
diff --git a/gemini.prj b/gemini.prj
new file mode 100644
index 0000000..7a52da9
--- /dev/null
+++ b/gemini.prj
@@ -0,0 +1,82 @@
+{
+ "Root":{
+ "Folders":[{
+ "Folders":[],
+ "Name":"+samples",
+ "Files":[{
+ "filename":".\\samples\\sample1.gmi",
+ "enabled":"1"
+ }]
+ }],
+ "Name":"+gemini (target.exe)",
+ "Files":[{
+ "filename":".\\dumb_render.f90",
+ "enabled":"1"
+ },{
+ "filename":".\\files.f90",
+ "enabled":"1"
+ },{
+ "filename":".\\layout.f90",
+ "enabled":"1"
+ },{
+ "filename":".\\render.f90",
+ "enabled":"1"
+ },{
+ "filename":".\\test.f90",
+ "enabled":"1"
+ }]
+ },
+ "Name":"gemini (target.exe)",
+ "Options":{
+ "Compiler Options":{
+ "Fortran Flags":"",
+ "Link Flags":"",
+ "C Flags":""
+ },
+ "Architecture":1,
+ "Type":0,
+ "Revision":2,
+ "Windows GUI":0,
+ "File Options":{
+ "Library Directories":["Default Add-On Directory"],
+ "Build Directory":"build",
+ "Module Directory":"modules",
+ "Include Directories":["Default Add-On Include Directory"]
+ },
+ "Target":"target.exe",
+ "Fortran Options":{
+ "Use C Preprocessor":"false",
+ "Runtime Diagnostics":"false",
+ "Cray Pointers":"false",
+ "Enable OpenMP":"false",
+ "Enable Coarrays":"false",
+ "Default Double for Real":"false"
+ },
+ "Code Generation Options":{
+ "CPU Specific":"false",
+ "Processor":"generic",
+ "Aggressive Loops":"false",
+ "Debugging":"true",
+ "Optimization Mode":0,
+ "Profiling":"false"
+ },
+ "Build Dependencies":1,
+ "Launch Options":{
+ "Working Directory":"",
+ "Launch Using MPI":"false",
+ "Keep Console":"true",
+ "External Console":"false",
+ "Command Line Arguments":"-g samples\\sample1.gmi",
+ "Build Before Launch":"true"
+ },
+ "Build Options":{
+ "Makefile":"Makefile",
+ "Auto Makefile":"true"
+ },
+ "Linker Options":{
+ "Static Linking Mode":7,
+ "Link MPI Library":"false",
+ "Link LAPACK":0
+ }
+ }
+} \ No newline at end of file
diff --git a/layout.f90 b/layout.f90
new file mode 100644
index 0000000..5d6f200
--- /dev/null
+++ b/layout.f90
@@ -0,0 +1,51 @@
+module layout
+implicit none
+
+ integer, parameter::line_type_text = 1
+ integer, parameter::line_type_preformatted = 2
+ integer, parameter::line_type_link = 3
+ integer, parameter::line_type_indicator = 99
+
+ type::line
+ character(len=:), allocatable::text
+ integer::line_type
+
+ type(line), pointer::next
+ end type line
+
+contains
+
+ subroutine layout_lines(first_line, rendering_engine)
+ use render
+ implicit none
+
+ type(line), pointer::first_line
+ class(renderer)::rendering_engine
+
+ type(line), pointer::walker
+ logical::laying_out
+
+ walker => first_line
+ laying_out = .true.
+
+ do while(laying_out)
+ select case (walker%line_type)
+
+ case (line_type_text)
+ call render_proportional(rendering_engine, walker%text)
+
+ case (line_type_preformatted)
+ call render_preformatted(rendering_engine, walker%text)
+
+ end select
+
+ laying_out = associated(walker%next)
+ if(laying_out) then
+ walker => walker%next
+ end if
+
+ end do
+
+ end subroutine layout_lines
+
+end module layout \ No newline at end of file
diff --git a/render.f90 b/render.f90
new file mode 100644
index 0000000..2913507
--- /dev/null
+++ b/render.f90
@@ -0,0 +1,173 @@
+module render
+implicit none
+
+ type, abstract :: renderer
+
+ integer::y
+ integer::max_width
+
+ contains
+
+ procedure::render_proportional
+
+ procedure(initialize), deferred::initialize
+
+ procedure(calculate_width), deferred::text_width
+ procedure(calculate_height), deferred::text_height
+ procedure(calculate_visibility), deferred::is_text_visible
+ procedure(draw_text), deferred::draw_porportional
+
+ procedure(calculate_width), deferred::preformatted_width
+ procedure(calculate_height), deferred::preformatted_height
+ procedure(calculate_visibility), deferred::is_preformatted_visible
+ procedure(draw_text), deferred::draw_preformatted
+
+ end type renderer
+
+ abstract interface
+ subroutine initialize(self)
+ import::renderer
+ class(renderer)::self
+ end subroutine initialize
+ end interface
+
+ abstract interface
+ function calculate_width(self, text)
+ import::renderer
+ class(renderer)::self
+ character(*), intent(in)::text
+ integer::calculate_width
+ end function calculate_width
+ end interface
+
+ abstract interface
+ function calculate_height(self, text)
+ import::renderer
+ class(renderer)::self
+ character(*), intent(in)::text
+ integer::calculate_height
+ end function calculate_height
+ end interface
+
+ abstract interface
+ subroutine draw_text(self, text)
+ import::renderer
+ class(renderer)::self
+ character(*), intent(in)::text
+ end subroutine draw_text
+ end interface
+
+ abstract interface
+ function calculate_visibility(self, text)
+ import::renderer
+ class(renderer)::self
+ character(*), intent(in)::text
+ logical::calculate_visibility
+ end function calculate_visibility
+ end interface
+
+contains
+
+ function width_of_line(r, text, startpos, endpos)
+ implicit none
+
+ class(renderer)::r
+ character(*), intent(in)::text
+ integer, intent(in)::startpos, endpos
+ integer::width_of_line
+
+ integer::my_start, my_end
+
+ my_end = endpos
+ if(endpos <= 0) then
+ my_end = len_trim(text)
+ end if
+
+ my_start = startpos
+ if(startpos <= 0) then
+ my_start = 1
+ end if
+
+ if(my_end <= my_start) then
+ width_of_line = 0
+ else
+ width_of_line = r%text_width(text(my_start:my_end))
+ end if
+
+ end function width_of_line
+
+ function wrap_line(r, text, startpos) result(endpos)
+ implicit none
+
+ class(renderer)::r
+ character(*), intent(in)::text
+ integer, intent(in)::startpos
+ integer::endpos
+ integer::my_start
+ integer::w
+
+ my_start = startpos
+ if(startpos == 0) then
+ my_start = 1
+ end if
+
+ endpos = len_trim(text)
+ w = width_of_line(r, text, my_start, endpos)
+ do while(w > r%max_width)
+
+ endpos = endpos - 1
+ do while(text(endpos:endpos) /= ' ' .and. text(endpos:endpos) /= '-')
+ endpos = endpos - 1
+ end do
+
+ w = width_of_line(r, text, my_start, endpos)
+ end do
+
+ end function wrap_line
+
+ subroutine render_proportional(r, text)
+ implicit none
+
+ class(renderer)::r
+ character(*)::text
+
+ integer::startpos, endpos
+
+ if(len_trim(text) == 0) then
+ if(r%is_text_visible(" ")) then
+ call r%draw_porportional("")
+ end if
+ r%y = r%y + r%text_height(" ")
+ else
+ startpos = 1
+ endpos = wrap_line(r, text, startpos)
+ do while(endpos > startpos)
+ if(r%is_text_visible(text(startpos:endpos))) then
+ call r%draw_porportional(text(startpos:endpos))
+ end if
+ r%y = r%y + r%text_height(text(startpos:endpos))
+
+ ! Advance string positions
+ startpos = endpos+1
+ do while(text(startpos:startpos) == ' ')
+ startpos = startpos + 1
+ end do
+ endpos = wrap_line(r, text, startpos)
+ end do
+ end if
+
+ end subroutine render_proportional
+
+ subroutine render_preformatted(r, text)
+ implicit none
+
+ class(renderer)::r
+ character(*)::text
+
+ if(r%is_preformatted_visible(text)) then
+ call r%draw_preformatted(text)
+ end if
+ r%y = r%y + r%preformatted_height(text)
+
+ end subroutine render_preformatted
+end module render
diff --git a/samples/sample1.gmi b/samples/sample1.gmi
new file mode 100644
index 0000000..a7de966
--- /dev/null
+++ b/samples/sample1.gmi
@@ -0,0 +1,14 @@
+# Welcome to a Sample Page
+
+This page is actual a simple sample of a gemini text file.
+
+```
+ 5 4 3 2 1
+
+ Tada!
+```
+
+Margaux sat alone on the old stone wall running along the dirt road in front of her house. The sun still shined brightly in the late afternoon, and she relished its warmth. As summer was slowly waning, these days would be rarer still.
+
+Reaching into a bowl, she absently threw bread chunks to the six large geese that stared at her expectantly from the road's birm. Looking eastward along the road and adjoining plains and farmlands, she hoped to see a man approach on foot. Henry was due back shortly.
+
diff --git a/test.f90 b/test.f90
new file mode 100644
index 0000000..df42126
--- /dev/null
+++ b/test.f90
@@ -0,0 +1,60 @@
+program test_render
+use file_handling
+use layout
+use dumb_render
+implicit none
+
+ character(256)::path, arg
+ integer::i
+
+ type(line), pointer::first_line
+ integer::iostatus
+ character(32)::mime
+
+ type(dumb_renderer)::r
+
+ if(command_argument_count() < 1) then
+ call usage()
+ stop 0
+ end if
+
+ mime = "text/plain"
+
+ do i = 1, command_argument_count()
+ call get_command_argument(i, arg)
+ if(trim(arg) == "-g") then
+ mime = "text/gemini"
+ else if(arg(1:1) == "-") then
+ Print *, "Unknown option: "//trim(arg)
+ Print *, " "
+ call usage()
+ stop 0
+ else
+ path = arg
+ end if
+ end do
+
+ first_line => load_filename(trim(path), iostatus, mime)
+ if(iostatus == 0) then
+ call r%initialize()
+ call layout_lines(first_line, r)
+ else
+ Print *, "Error occurred while loading "//trim(path)
+ end if
+
+ contains
+
+ subroutine usage()
+ implicit none
+
+ character(256)::exe
+
+ call get_command_argument(0, exe)
+ Print *, "Usage: "//trim(exe)//" [-g] <file to load>"
+ Print *, " "
+ Print *, " -g Treat file as text/gemini"
+ Print *, " "
+
+ end subroutine usage
+
+end program test_render \ No newline at end of file