Commit 2100b4ce152e9c70b3ce8760112d5a24ead4e52d

Authored by Jay Berkenbilt
1 parent 6a4117ad

Allow qpdf to be built on systems without wchar_t (fixes #406)

ChangeLog
1 2020-04-03 Jay Berkenbilt <ejb@ql.org> 1 2020-04-03 Jay Berkenbilt <ejb@ql.org>
2 2
  3 + * Allow qpdf to be built on systems without wchar_t. All "normal"
  4 + systems have wchar_t because it is part of the C++ standard, but
  5 + there are some stripped down environments that don't have it. See
  6 + README.md (search for wchar_t) for instructions and a discussion.
  7 + Fixes #406.
  8 +
3 * Add two extra optional arguments to 9 * Add two extra optional arguments to
4 QPDFPageObjectHelper::placeFormXObject to control whether the 10 QPDFPageObjectHelper::placeFormXObject to control whether the
5 placed item is allowed to be shrunk or expanded to fit within or 11 placed item is allowed to be shrunk or expanded to fit within or
README.md
@@ -75,6 +75,20 @@ make install @@ -75,6 +75,20 @@ make install
75 75
76 Packagers may set DESTDIR, in which case make install will install inside of DESTDIR, as is customary with many packages. For more detailed general information, see the "INSTALL" file in this directory. If you are already accustomed to building and installing software that uses autoconf, there's nothing new for you in the INSTALL file. Note that qpdf uses `autoconf` but not `automake`. We have our own system of Makefiles that allows cross-directory dependencies, doesn't use recursive make, and works better on non-UNIX platforms. 76 Packagers may set DESTDIR, in which case make install will install inside of DESTDIR, as is customary with many packages. For more detailed general information, see the "INSTALL" file in this directory. If you are already accustomed to building and installing software that uses autoconf, there's nothing new for you in the INSTALL file. Note that qpdf uses `autoconf` but not `automake`. We have our own system of Makefiles that allows cross-directory dependencies, doesn't use recursive make, and works better on non-UNIX platforms.
77 77
  78 +# Building without wchar_t
  79 +
  80 +Executive summary: manually define -DQPDF_NO_WCHAR_T in your build if you are building on a system without wchar_t. For details, read the rest of this section.
  81 +
  82 +While wchar_t is part of the C++ standard library and should be present on virtually every system, there are some stripped down systems, such as those targetting certain embedded environments, that lack wchar_t. Internally, qpdf uses UTF-8 encoding for everything, so there is nothing important in qpdf's API that uses wchar_t. However, there is a helper method for converting between wchar_t* and char* that uses wchar_t.
  83 +
  84 +If you are building in an environment that does not support wchar_t, you can define the preprocessor symbol QPDF_NO_WCHAR_T in your build. This will work whether you are building qpdf and need to avoid compiling the code that uses wchar_t or whether you are building client code that uses qpdf.
  85 +
  86 +For example, to build qpdf on a system without wchar_t, be sure that -DQPDF_NO_WCHAR_T is part of your CXXFLAGS. Similar techniques will work in other places.
  87 +
  88 +Note that, when you build code with libqpdf, it is *not necessary* to have the definition of QPDF_NO_WCHAR_T in your build match what was defined when the library was built as long as you are not calling QUtil::call_main_from_wmain in your code. In other words, if your qpdf library was built on a system without wchar_t and you are using that system to build at some later time after wchar_t was available, as long as you don't call the function that uses it, you can just build normally.
  89 +
  90 +Note that QPDF_NO_WCHAR_T is never defined by autoconf or any other automated method. There is a hard rule in qpdf that values determined by autoconf are not available in the public API. This is because there is never a guarantee or even expectation that those values will match between the system on which qpdf was build and the system on which a user is building code with libqpdf.
  91 +
78 # Building on Windows 92 # Building on Windows
79 93
80 QPDF is known to build and pass its test suite with mingw (latest version tested: gcc 7.2.0), mingw64 (latest version tested: 7.2.0) and Microsoft Visual C++ 2015, both 32-bit and 64-bit versions. MSYS2 is required to build as well in order to get make and other related tools. See [README-windows.md](README-windows.md) for details on how to build under Windows. 94 QPDF is known to build and pass its test suite with mingw (latest version tested: gcc 7.2.0), mingw64 (latest version tested: 7.2.0) and Microsoft Visual C++ 2015, both 32-bit and 64-bit versions. MSYS2 is required to build as well in order to get make and other related tools. See [README-windows.md](README-windows.md) for details on how to build under Windows.
include/qpdf/QUtil.hh
@@ -357,12 +357,20 @@ namespace QUtil @@ -357,12 +357,20 @@ namespace QUtil
357 QPDF_DLL 357 QPDF_DLL
358 std::vector<int> parse_numrange(char const* range, int max); 358 std::vector<int> parse_numrange(char const* range, int max);
359 359
  360 +#ifndef QPDF_NO_WCHAR_T
  361 + // If you are building qpdf on a stripped down system that doesn't
  362 + // have wchar_t, such as may be the case in some embedded
  363 + // environments, you may define QPDF_NO_WCHAR_T in your build.
  364 + // This symbol is never defined automatically. Search for wchar_t
  365 + // in qpdf's top-level README.md file for details.
  366 +
360 // Take an argv array consisting of wchar_t, as when wmain is 367 // Take an argv array consisting of wchar_t, as when wmain is
361 // invoked, convert all UTF-16 encoded strings to UTF-8, and call 368 // invoked, convert all UTF-16 encoded strings to UTF-8, and call
362 // another main. 369 // another main.
363 QPDF_DLL 370 QPDF_DLL
364 int call_main_from_wmain(int argc, wchar_t* argv[], 371 int call_main_from_wmain(int argc, wchar_t* argv[],
365 std::function<int(int, char*[])> realmain); 372 std::function<int(int, char*[])> realmain);
  373 +#endif // QPDF_NO_WCHAR_T
366 }; 374 };
367 375
368 #endif // QUTIL_HH 376 #endif // QUTIL_HH
libqpdf/QUtil.cc
@@ -2342,8 +2342,10 @@ QUtil::possible_repaired_encodings(std::string supplied) @@ -2342,8 +2342,10 @@ QUtil::possible_repaired_encodings(std::string supplied)
2342 return t; 2342 return t;
2343 } 2343 }
2344 2344
  2345 +#ifndef QPDF_NO_WCHAR_T
2345 int 2346 int
2346 -QUtil::call_main_from_wmain(int argc, wchar_t* argv[], std::function<int(int, char*[])> realmain) 2347 +QUtil::call_main_from_wmain(int argc, wchar_t* argv[],
  2348 + std::function<int(int, char*[])> realmain)
2347 { 2349 {
2348 // argv contains UTF-16-encoded strings with a 16-bit wchar_t. 2350 // argv contains UTF-16-encoded strings with a 16-bit wchar_t.
2349 // Convert this to UTF-8-encoded strings for compatibility with 2351 // Convert this to UTF-8-encoded strings for compatibility with
@@ -2376,3 +2378,4 @@ QUtil::call_main_from_wmain(int argc, wchar_t* argv[], std::function&lt;int(int, ch @@ -2376,3 +2378,4 @@ QUtil::call_main_from_wmain(int argc, wchar_t* argv[], std::function&lt;int(int, ch
2376 new_argv[argc] = 0; 2378 new_argv[argc] = 0;
2377 return realmain(argc, new_argv); 2379 return realmain(argc, new_argv);
2378 } 2380 }
  2381 +#endif // QPDF_NO_WCHAR_T
libtests/build.mk
@@ -13,6 +13,7 @@ BINS_libtests = \ @@ -13,6 +13,7 @@ BINS_libtests = \
13 input_source \ 13 input_source \
14 json \ 14 json \
15 lzw \ 15 lzw \
  16 + main_from_wmain \
16 matrix \ 17 matrix \
17 md5 \ 18 md5 \
18 numrange \ 19 numrange \
libtests/main_from_wmain.cc 0 → 100644
  1 +#include <qpdf/QUtil.hh>
  2 +#include <iostream>
  3 +
  4 +#ifndef QPDF_NO_WCHAR_T
  5 +void wmain_test()
  6 +{
  7 + auto realmain = [](int argc, char* argv[]) {
  8 + for (int i = 0; i < argc; ++i) {
  9 + std::cout << argv[i] << std::endl;
  10 + } return 0;
  11 + };
  12 + wchar_t* argv[3];
  13 + argv[0] = const_cast<wchar_t*>(L"ascii");
  14 + argv[1] = const_cast<wchar_t*>(L"10 \xf7 2 = 5");
  15 + argv[2] = const_cast<wchar_t*>(L"qwww\xf7\x03c0");
  16 + QUtil::call_main_from_wmain(3, argv, realmain);
  17 +}
  18 +#endif // QPDF_NO_WCHAR_T
  19 +
  20 +int main(int argc, char* argv[])
  21 +{
  22 +#ifndef QPDF_NO_WCHAR_T
  23 + try
  24 + {
  25 + wmain_test();
  26 + }
  27 + catch (std::exception& e)
  28 + {
  29 + std::cout << "unexpected exception: " << e.what() << std::endl;
  30 + }
  31 +#endif // QPDF_NO_WCHAR_T
  32 +
  33 + return 0;
  34 +}
libtests/qtest/qutil.test
@@ -15,4 +15,20 @@ $td-&gt;runtest(&quot;QUtil&quot;, @@ -15,4 +15,20 @@ $td-&gt;runtest(&quot;QUtil&quot;,
15 $td->EXIT_STATUS => 0}, 15 $td->EXIT_STATUS => 0},
16 $td->NORMALIZE_NEWLINES | $td->RM_WS_ONLY_LINES); 16 $td->NORMALIZE_NEWLINES | $td->RM_WS_ONLY_LINES);
17 17
18 -$td->report(1); 18 +my $mfw = `main_from_wmain`;
  19 +if ($mfw eq '')
  20 +{
  21 + $td->runtest("skipping main_from_wmain",
  22 + {$td->STRING => ""},
  23 + {$td->STRING => ""})
  24 +}
  25 +else
  26 +{
  27 + $td->runtest("main_from_wmain",
  28 + {$td->COMMAND => "main_from_wmain"},
  29 + {$td->FILE => "wmain.out",
  30 + $td->EXIT_STATUS => 0},
  31 + $td->NORMALIZE_NEWLINES | $td->RM_WS_ONLY_LINES);
  32 +}
  33 +
  34 +$td->report(2);
libtests/qtest/qutil/qutil.out
@@ -105,7 +105,3 @@ rename file @@ -105,7 +105,3 @@ rename file
105 create file 105 create file
106 rename over existing 106 rename over existing
107 delete file 107 delete file
108 ----- wmain  
109 -ascii  
110 -10 ÷ 2 = 5  
111 -qwww÷π  
libtests/qtest/qutil/wmain.out 0 → 100644
  1 +ascii
  2 +10 ÷ 2 = 5
  3 +qwww÷π
libtests/qutil.cc
@@ -543,17 +543,6 @@ void rename_delete_test() @@ -543,17 +543,6 @@ void rename_delete_test()
543 assert_no_file("old\xcf\x80.~tmp"); 543 assert_no_file("old\xcf\x80.~tmp");
544 } 544 }
545 545
546 -void wmain_test()  
547 -{  
548 - auto realmain = [](int argc, char* argv[]) {  
549 - for (int i = 0; i < argc; ++i) { std::cout << argv[i] << std::endl; } return 0; };  
550 - wchar_t* argv[3];  
551 - argv[0] = const_cast<wchar_t*>(L"ascii");  
552 - argv[1] = const_cast<wchar_t*>(L"10 \xf7 2 = 5");  
553 - argv[2] = const_cast<wchar_t*>(L"qwww\xf7\x03c0");  
554 - QUtil::call_main_from_wmain(3, argv, realmain);  
555 -}  
556 -  
557 int main(int argc, char* argv[]) 546 int main(int argc, char* argv[])
558 { 547 {
559 try 548 try
@@ -584,8 +573,6 @@ int main(int argc, char* argv[]) @@ -584,8 +573,6 @@ int main(int argc, char* argv[])
584 hex_encode_decode_test(); 573 hex_encode_decode_test();
585 std::cout << "---- rename/delete" << std::endl; 574 std::cout << "---- rename/delete" << std::endl;
586 rename_delete_test(); 575 rename_delete_test();
587 - std::cout << "---- wmain" << std::endl;  
588 - wmain_test();  
589 } 576 }
590 catch (std::exception& e) 577 catch (std::exception& e)
591 { 578 {
manual/qpdf-manual.xml
@@ -240,6 +240,16 @@ make @@ -240,6 +240,16 @@ make
240 <filename>README-windows.md</filename>. 240 <filename>README-windows.md</filename>.
241 </para> 241 </para>
242 <para> 242 <para>
  243 + While <type>wchar_t</type> is part of the C++ standard, qpdf uses
  244 + it in only one place in the public API, and it's just in a helper
  245 + function. It is possible to build qpdf on a system that doesn't
  246 + have <type>wchar_t</type>, and it's also possible to compile a
  247 + program that uses qpdf on a system without <type>wchar_t</type> as
  248 + long as you don't call that one method. This is a very unusual
  249 + situation. For a detailed discussion, please see the top-level
  250 + README.md file in qpdf's source distribution.
  251 + </para>
  252 + <para>
243 There are some other things you can do with the build. Although 253 There are some other things you can do with the build. Although
244 qpdf uses <application>autoconf</application>, it does not use 254 qpdf uses <application>autoconf</application>, it does not use
245 <application>automake</application> but instead uses a 255 <application>automake</application> but instead uses a