Skip to content

Commit 0dedbcb

Browse files
authored
Merge pull request #18 from cxw42/issue15
Text justification; font name; space between paragraphs
2 parents b91d484 + b731631 commit 0dedbcb

17 files changed

+13786
-195
lines changed

Makefile.am

+5
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,8 @@ prep:
3737
+$(MAKE) check
3838
+$(MAKE) all build-tests && prove -v
3939
+$(MAKE) distcheck
40+
41+
# Used by coverage.sh --- remove the existing code-coverage data.
42+
remove-code-coverage-data:
43+
-rm -rf "$(CODE_COVERAGE_OUTPUT_FILE)" "$(CODE_COVERAGE_OUTPUT_FILE).tmp" "$(CODE_COVERAGE_OUTPUT_DIRECTORY)"
44+
-find . -name "*.gcda" -delete

configure.ac

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
dnl === Basic setup =======================================================
2-
AC_INIT([pfft markdown-to-PDF converter], [0.0.3], [], [pfft], [https://github.com/cxw42/pfft])
2+
AC_INIT([pfft markdown-to-PDF converter], [0.0.4], [], [pfft], [https://github.com/cxw42/pfft])
33
AC_PREREQ([2.65])
44
AC_COPYRIGHT([Copyright (C) 2020 Christopher White])
55
AC_CONFIG_SRCDIR([rules.mk]) dnl make sure the srcdir is correctly specified

coverage.sh

+14-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
#!/bin/bash
22
set -x
33
set -eEuo pipefail
4-
./bootstrap
5-
./configure --enable-code-coverage USER_VALAFLAGS='-g' CFLAGS='-g -O0' "$@"
4+
5+
# Reconfigure if necessary so that coverage is enabled
6+
if [[ ! -x ./config.status ]] || \
7+
! ./config.status --config | grep -- '--enable-code-coverage'
8+
then
9+
./bootstrap
10+
./configure --enable-code-coverage USER_VALAFLAGS='-g' CFLAGS='-g -O0' "$@"
11+
fi
12+
13+
# Clean up from old runs, e.g., test runs of ./src/pfft compiled with
14+
# coverage turned on.
15+
make -j4 remove-code-coverage-data
16+
17+
# Check it
618
make -j4 check-code-coverage

rules.mk

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ MY_vala_TESTS = \
5959
001-sanity-t \
6060
010-core-util-t \
6161
020-registry-t \
62+
021-registry-classmap-t \
6263
050-core-el-t \
6364
055-core-units-t \
6465
060-core-template-t \

src/app/pfft.vala

+5-103
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,6 @@ namespace My {
88

99
extern void init_gstreamer(); // from pfft-shim.c
1010

11-
// Types {{{1
12-
13-
/**
14-
* Map from friendly names to GTypes.
15-
*
16-
* Used to store and sort readers and writers.
17-
* This is a typedef.
18-
*/
19-
private class ClassMap : Gee.TreeMap<string, GLib.Type> { }
20-
21-
// }}}1
22-
2311
/**
2412
* Main application class for pfft
2513
*/
@@ -134,7 +122,7 @@ namespace My {
134122
{
135123
Intl.setlocale (LocaleCategory.ALL, ""); // init locale from environment
136124
init_gstreamer();
137-
linit();
125+
Log.linit();
138126
}
139127

140128
/**
@@ -212,8 +200,8 @@ namespace My {
212200
Reader reader;
213201
var reader_name = opt_reader_name ?? reader_default_;
214202
try {
215-
reader = create_instance(
216-
readers_, reader_name, opt_reader_options) as Reader;
203+
reader = readers_.create_instance(
204+
reader_name, template_, opt_reader_options) as Reader;
217205
} catch(KeyFileError e) {
218206
printerr ("Could not create reader: %s\n", e.message);
219207
return 1;
@@ -222,8 +210,8 @@ namespace My {
222210
Writer writer;
223211
var writer_name = opt_writer_name ?? writer_default_;
224212
try {
225-
writer = create_instance(
226-
writers_, writer_name, opt_writer_options) as Writer;
213+
writer = writers_.create_instance(
214+
writer_name, template_, opt_writer_options) as Writer;
227215
} catch(KeyFileError e) {
228216
printerr ("Could not create writer: %s\n", e.message);
229217
return 1;
@@ -417,92 +405,6 @@ namespace My {
417405
return sb.str;
418406
} // get_classmap_help
419407

420-
/**
421-
* Create an instance and set its properties.
422-
*
423-
* Sets properties from template_ first, then from @options.
424-
*/
425-
private Object create_instance(ClassMap m, string class_name,
426-
string[]? options) throws KeyFileError
427-
{
428-
if(!m.has_key(class_name)) {
429-
throw new KeyFileError.KEY_NOT_FOUND(
430-
"%s: Class not registered".printf(class_name));
431-
}
432-
433-
var type = m.get(class_name);
434-
Object retval = Object.new(type);
435-
set_props_from_template(type, retval, template_);
436-
437-
if(options == null) {
438-
return retval; // *** EXIT POINT ***
439-
}
440-
441-
// property accessor for the instance we are creating
442-
ObjectClass ocl = (ObjectClass) type.class_ref ();
443-
444-
// Assign the properties
445-
var num_opts = (options == null) ? 0 : strv_length(options);
446-
for(int i=0; i<num_opts; ++i) {
447-
var optspec = options[i];
448-
var nv = optspec.split("=", 2);
449-
if(nv.length != 2) {
450-
throw new KeyFileError.INVALID_VALUE(
451-
"%s: Invalid option %s".printf(class_name, optspec));
452-
}
453-
454-
// print("Trying %p->%s := %s\n", retval, nv[0], nv[1]);
455-
var prop = ocl.find_property(nv[0]);
456-
if(prop == null || prop.get_name()[0] == 'P') { // skip unknown, private
457-
throw new KeyFileError.KEY_NOT_FOUND(
458-
"%s: %s is not an option I understand".printf(
459-
class_name, nv[0]));
460-
}
461-
462-
var val = GLib.Value(prop.value_type);
463-
if(!deserialize_value(ref val, nv[1])) {
464-
throw new KeyFileError.INVALID_VALUE(
465-
"%s: Invalid value %s for option %s".printf(
466-
class_name, nv[1], nv[0]));
467-
}
468-
469-
retval.set_property(nv[0], val);
470-
linfoo(retval, "Set property %s from command line to %s",
471-
nv[0], val.type() == typeof(string) ? @"'$(val.get_string())'" :
472-
Gst.Value.serialize(val));
473-
474-
} // foreach option
475-
476-
return retval;
477-
} // create_instance()
478-
479-
private void set_props_from_template(GLib.Type instance_type,
480-
Object instance, Template tmpl)
481-
{
482-
// property accessor for the instance we are creating
483-
ObjectClass ocl = (ObjectClass) instance_type.class_ref ();
484-
485-
// property accessor for the template
486-
ObjectClass tocl = (ObjectClass) tmpl.get_type().class_ref ();
487-
488-
// Set properties from the template
489-
foreach(var tprop in tocl.list_properties()) {
490-
string propname = tprop.get_name();
491-
ldebugo(instance, "Trying template property %s", propname);
492-
var prop = ocl.find_property(propname);
493-
if(prop == null || propname[0] == 'P' || prop.value_type != tprop.value_type) {
494-
ldebugo(instance, "--- skipping");
495-
continue;
496-
}
497-
498-
Value v = Value(prop.value_type);
499-
tmpl.get_property(propname, ref v);
500-
instance.set_property(propname, v);
501-
linfoo(instance, "Set property %s from template to %s",
502-
propname, Gst.Value.serialize(v));
503-
}
504-
} // set_props_from_template()
505-
506408
// }}}1
507409
} // class App
508410
} // My

src/core/registry.vala

+97
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,103 @@
88
// Copyright (c) 2020 Christopher White. All rights reserved.
99
// SPDX-License-Identifier: BSD-3-Clause
1010

11+
using My.Log;
12+
1113
namespace My {
14+
15+
/**
16+
* Map from friendly names to GTypes.
17+
*
18+
* Used to store and sort readers and writers.
19+
*/
20+
public class ClassMap : Gee.TreeMap<string, GLib.Type> {
21+
22+
/** Set object properties from 'name=value' pairs */
23+
private static void set_options_on(Object target, string[] options,
24+
string class_name)
25+
throws KeyFileError
26+
{
27+
// property accessor for the instance we are creating
28+
ObjectClass ocl = (ObjectClass) target.get_type().class_ref ();
29+
30+
// Assign the properties
31+
var num_opts = (options == null) ? 0 : strv_length(options);
32+
for(int i=0; i<num_opts; ++i) {
33+
var optspec = options[i];
34+
var nv = optspec.split("=", 2);
35+
if(nv.length != 2 || nv[0].length < 1 || nv[1].length < 1) {
36+
throw new KeyFileError.INVALID_VALUE(
37+
"%s: Invalid option %s".printf(class_name, optspec));
38+
}
39+
40+
var prop = ocl.find_property(nv[0]);
41+
if(prop == null || prop.get_name()[0] == 'P') { // skip unknown, private
42+
throw new KeyFileError.KEY_NOT_FOUND(
43+
"%s: %s is not an option I understand".printf(
44+
class_name, nv[0]));
45+
}
46+
47+
var val = GLib.Value(prop.value_type);
48+
if(!deserialize_value(ref val, nv[1])) {
49+
throw new KeyFileError.INVALID_VALUE(
50+
"%s: Invalid value %s for option %s".printf(
51+
class_name, nv[1], nv[0]));
52+
}
53+
54+
// TODO unit conversion for properties with dimen values, a la
55+
// Template.
56+
57+
target.set_property(nv[0], val);
58+
linfoo(target, "Set property %s from command line to %s",
59+
nv[0], val.type() == typeof(string) ? @"'$(val.get_string())'" :
60+
Gst.Value.serialize(val) // LCOV_EXCL_LINE - can't guarantee this will fire during tests
61+
);
62+
63+
} // foreach option
64+
} // set_options_on()
65+
66+
/**
67+
* Create an instance and set its properties.
68+
*
69+
* Sets properties from @template first, then from @options.
70+
* @param m The class registry to use
71+
* @param class_name The name of the class to instantiate.
72+
* This may be different from the name in the
73+
* source code.
74+
* @param template Optional My.Template. Properties that exist
75+
* both in the template and the target class
76+
* will be copied from the target to the
77+
* new instance.
78+
* @param options Optional 'property=value' assigments.
79+
* @return The new instance, or null.
80+
*/
81+
public Object? create_instance(string class_name,
82+
Template? template, string[]? options) throws KeyFileError
83+
{
84+
if(!this.has_key(class_name)) {
85+
throw new KeyFileError.KEY_NOT_FOUND(
86+
"%s: Class not registered".printf(class_name));
87+
}
88+
89+
var type = this.get(class_name);
90+
Object retval = Object.new(type);
91+
92+
if(retval == null) {
93+
return null; // LCOV_EXCL_LINE - I don't know any way to force this to happen during testing
94+
}
95+
96+
if(template!=null) {
97+
template.set_props_on(retval);
98+
}
99+
100+
if(options != null) {
101+
set_options_on(retval, options, class_name);
102+
}
103+
104+
return retval;
105+
} // create_instance()
106+
} // class ClassMap
107+
12108
/**
13109
* Get a reference to the registry of classes.
14110
*
@@ -50,4 +146,5 @@ namespace My {
50146
* Out of all classes that implement a particular interface, that is.
51147
*/
52148
public const string CLASS_META_NICK_DEFAULT = "default";
149+
53150
} // My

src/core/template.vala

+41-1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ namespace My {
6666
public string footerr { get; set; default = ""; }
6767

6868
// Font parameters
69+
[Description(nick = "Font name", blurb = "Font of body text")]
70+
public string fontname { get; set; default = "Serif"; }
6971
[Description(nick = "Font size (pt.)", blurb = "Size of body text, in points (72/in.)")]
7072
public double fontsizeT { get; set; default = 12; }
7173

@@ -316,6 +318,7 @@ namespace My {
316318
} // footer
317319

318320
if(data.has_group("font")) {
321+
set_from_file("fontname", "font", "name");
319322
set_from_file("fontsizeT", "font", "size");
320323
ldebugo(this, "font size %f pt", fontsizeT);
321324
}
@@ -328,6 +331,43 @@ namespace My {
328331
ldebugo(this, "Done processing template file %s", filename);
329332
} // Template.from_file()
330333

334+
// --- Using templates -------------------------
335+
336+
/**
337+
* Copy property values into an object.
338+
*
339+
* For each non-private property that exists both in this template and
340+
* the target instance, the value will be copied from the target to the
341+
* new instance.
342+
*
343+
* @param target The instance to update. Modified in place.
344+
*/
345+
public void set_props_on(Object target)
346+
{
347+
// property accessor for the instance we are creating
348+
ObjectClass ocl = (ObjectClass) target.get_type().class_ref ();
349+
350+
// property accessor for the template
351+
ObjectClass tocl = (ObjectClass) this.get_type().class_ref ();
352+
353+
// Set properties from the template
354+
foreach(var tprop in tocl.list_properties()) {
355+
string propname = tprop.get_name();
356+
ldebugo(target, "Trying template property %s", propname);
357+
var prop = ocl.find_property(propname);
358+
if(prop == null || propname[0] == 'P' || prop.value_type != tprop.value_type) {
359+
ldebugo(target, " --- skipping");
360+
continue;
361+
}
362+
363+
Value v = Value(prop.value_type);
364+
this.get_property(propname, ref v);
365+
target.set_property(propname, v);
366+
linfoo(target, "Set property %s from template to %s",
367+
propname, Gst.Value.serialize(v));
368+
}
369+
} // set_props_on()
370+
331371
} // class Template
332372

333-
}
373+
} // namespace My

src/logging/logging-c.c

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ GST_DEBUG_CATEGORY(my_log_category);
1111
void my_log_linit()
1212
{
1313
GST_DEBUG_CATEGORY_INIT(my_log_category, "pfft", 0, "");
14+
GST_INFO("pfft logging initialized");
1415
}
1516

1617
// the following is copied from

0 commit comments

Comments
 (0)