David PHAM-VAN

Add google fonts

@@ -56,3 +56,4 @@ package.json @@ -56,3 +56,4 @@ package.json
56 56
57 GeneratedPluginRegistrant.* 57 GeneratedPluginRegistrant.*
58 test/social_preview.png 58 test/social_preview.png
  59 +test/fonts.json
1 # Changelog 1 # Changelog
2 2
  3 +## 5.4.0
  4 +
  5 +- Add Google Fonts support
  6 +
3 ## 5.3.0 7 ## 5.3.0
4 8
5 - Fix raster crash on all OS. 9 - Fix raster crash on all OS.
@@ -7,7 +7,9 @@ import 'package:pdf/pdf.dart'; @@ -7,7 +7,9 @@ import 'package:pdf/pdf.dart';
7 import 'package:pdf/widgets.dart' as pw; 7 import 'package:pdf/widgets.dart' as pw;
8 import 'package:printing/printing.dart'; 8 import 'package:printing/printing.dart';
9 9
10 -void main() => runApp(const MyApp('Printing Demo')); 10 +Future<void> main() async {
  11 + runApp(const MyApp('Printing Demo'));
  12 +}
11 13
12 class MyApp extends StatelessWidget { 14 class MyApp extends StatelessWidget {
13 const MyApp(this.title); 15 const MyApp(this.title);
@@ -27,14 +29,24 @@ class MyApp extends StatelessWidget { @@ -27,14 +29,24 @@ class MyApp extends StatelessWidget {
27 } 29 }
28 30
29 Future<Uint8List> _generatePdf(PdfPageFormat format, String title) async { 31 Future<Uint8List> _generatePdf(PdfPageFormat format, String title) async {
30 - final pdf = pw.Document(); 32 + final pdf = pw.Document(version: PdfVersion.pdf_1_5, compress: true);
  33 + final font = await PdfGoogleFonts.nunitoExtraLight();
31 34
32 pdf.addPage( 35 pdf.addPage(
33 pw.Page( 36 pw.Page(
34 pageFormat: format, 37 pageFormat: format,
35 build: (context) { 38 build: (context) {
36 - return pw.Center(  
37 - child: pw.Text(title), 39 + return pw.Column(
  40 + children: [
  41 + pw.SizedBox(
  42 + width: double.infinity,
  43 + child: pw.FittedBox(
  44 + child: pw.Text(title, style: pw.TextStyle(font: font)),
  45 + ),
  46 + ),
  47 + pw.SizedBox(height: 20),
  48 + pw.Flexible(child: pw.FlutterLogo())
  49 + ],
38 ); 50 );
39 }, 51 },
40 ), 52 ),
@@ -15,7 +15,9 @@ @@ -15,7 +15,9 @@
15 */ 15 */
16 16
17 export 'src/asset_utils.dart'; 17 export 'src/asset_utils.dart';
  18 +export 'src/cache.dart';
18 export 'src/callback.dart'; 19 export 'src/callback.dart';
  20 +export 'src/fonts/gfonts.dart';
19 export 'src/preview/pdf_preview.dart'; 21 export 'src/preview/pdf_preview.dart';
20 export 'src/preview/pdf_preview_action.dart'; 22 export 'src/preview/pdf_preview_action.dart';
21 export 'src/printer.dart'; 23 export 'src/printer.dart';
@@ -15,8 +15,6 @@ @@ -15,8 +15,6 @@
15 */ 15 */
16 16
17 import 'dart:async'; 17 import 'dart:async';
18 -import 'dart:io';  
19 -import 'dart:typed_data';  
20 import 'dart:ui' as ui; 18 import 'dart:ui' as ui;
21 19
22 import 'package:flutter/rendering.dart' as rdr; 20 import 'package:flutter/rendering.dart' as rdr;
@@ -24,6 +22,8 @@ import 'package:flutter/services.dart'; @@ -24,6 +22,8 @@ import 'package:flutter/services.dart';
24 import 'package:pdf/pdf.dart'; 22 import 'package:pdf/pdf.dart';
25 import 'package:pdf/widgets.dart'; 23 import 'package:pdf/widgets.dart';
26 24
  25 +import 'cache.dart';
  26 +
27 /// Loads an image from a Flutter [ImageProvider] 27 /// Loads an image from a Flutter [ImageProvider]
28 /// into an [ImageProvider] instance 28 /// into an [ImageProvider] instance
29 Future<ImageProvider> flutterImageProvider( 29 Future<ImageProvider> flutterImageProvider(
@@ -66,77 +66,35 @@ Future<ImageProvider> flutterImageProvider( @@ -66,77 +66,35 @@ Future<ImageProvider> flutterImageProvider(
66 66
67 /// Loads a font from an asset bundle key. If used multiple times with the same font name, 67 /// Loads a font from an asset bundle key. If used multiple times with the same font name,
68 /// it will be included multiple times in the pdf file 68 /// it will be included multiple times in the pdf file
69 -Future<TtfFont> fontFromAssetBundle(String key, [AssetBundle? bundle]) async { 69 +Future<TtfFont> fontFromAssetBundle(
  70 + String key, {
  71 + AssetBundle? bundle,
  72 + bool cache = true,
  73 + PdfBaseCache? pdfCache,
  74 + bool protect = false,
  75 +}) async {
70 bundle ??= rootBundle; 76 bundle ??= rootBundle;
71 - final data = await bundle.load(key);  
72 - return TtfFont(data); 77 + final bytes = await bundle.load(key);
  78 + return TtfFont(bytes, protect: protect);
73 } 79 }
74 80
75 /// Load an image from an asset bundle key. 81 /// Load an image from an asset bundle key.
76 -Future<ImageProvider> imageFromAssetBundle(String key,  
77 - [AssetBundle? bundle]) async { 82 +Future<ImageProvider> imageFromAssetBundle(
  83 + String key, {
  84 + AssetBundle? bundle,
  85 + bool cache = true,
  86 + PdfImageOrientation? orientation,
  87 + double? dpi,
  88 + PdfBaseCache? pdfCache,
  89 +}) async {
78 bundle ??= rootBundle; 90 bundle ??= rootBundle;
79 - final data = await bundle.load(key);  
80 - return MemoryImage(data.buffer.asUint8List());  
81 -}  
82 -  
83 -final HttpClient _sharedHttpClient = HttpClient();  
84 -  
85 -/// Store network images in a cache  
86 -abstract class PdfBaseImageCache {  
87 - /// Create a network image cache  
88 - const PdfBaseImageCache();  
89 -  
90 - /// The default cache used when none specified  
91 - static final defaultCache = PdfMemoryImageCache();  
92 -  
93 - /// Add an image to the cache  
94 - Future<void> add(String key, Uint8List bytes);  
95 -  
96 - /// Retrieve an image from the cache  
97 - Future<Uint8List?> get(String key);  
98 -  
99 - /// Does the cache contains this image?  
100 - Future<bool> contains(String key); 91 + final bytes = await bundle.load(key);
101 92
102 - /// Remove an image from the cache  
103 - Future<void> remove(String key);  
104 -  
105 - /// Clear the cache  
106 - Future<void> clear();  
107 -}  
108 -  
109 -/// Memory image cache  
110 -class PdfMemoryImageCache extends PdfBaseImageCache {  
111 - /// Create a memory image cache  
112 - PdfMemoryImageCache();  
113 -  
114 - final _imageCache = <String, Uint8List>{};  
115 -  
116 - @override  
117 - Future<void> add(String key, Uint8List bytes) async {  
118 - _imageCache[key] = bytes;  
119 - }  
120 -  
121 - @override  
122 - Future<Uint8List?> get(String key) async {  
123 - return _imageCache[key];  
124 - }  
125 -  
126 - @override  
127 - Future<void> clear() async {  
128 - _imageCache.clear();  
129 - }  
130 -  
131 - @override  
132 - Future<bool> contains(String key) async {  
133 - return _imageCache.containsKey(key);  
134 - }  
135 -  
136 - @override  
137 - Future<void> remove(String key) async {  
138 - _imageCache.remove(key);  
139 - } 93 + return MemoryImage(
  94 + bytes.buffer.asUint8List(bytes.offsetInBytes, bytes.lengthInBytes),
  95 + orientation: orientation,
  96 + dpi: dpi,
  97 + );
140 } 98 }
141 99
142 /// Download an image from the network. 100 /// Download an image from the network.
@@ -146,28 +104,15 @@ Future<ImageProvider> networkImage( @@ -146,28 +104,15 @@ Future<ImageProvider> networkImage(
146 Map<String, String>? headers, 104 Map<String, String>? headers,
147 PdfImageOrientation? orientation, 105 PdfImageOrientation? orientation,
148 double? dpi, 106 double? dpi,
149 - PdfBaseImageCache? imageCache, 107 + PdfBaseCache? pdfCache,
150 }) async { 108 }) async {
151 - imageCache ??= PdfBaseImageCache.defaultCache;  
152 -  
153 - if (cache && await imageCache.contains(url)) {  
154 - return MemoryImage((await imageCache.get(url))!,  
155 - orientation: orientation, dpi: dpi);  
156 - }  
157 -  
158 - final request = await _sharedHttpClient.getUrl(Uri.parse(url));  
159 - headers?.forEach((String name, String value) {  
160 - request.headers.add(name, value);  
161 - });  
162 - final response = await request.close();  
163 - final builder = await response.fold(  
164 - BytesBuilder(), (BytesBuilder b, List<int> d) => b..add(d));  
165 - final List<int> data = builder.takeBytes();  
166 - final bytes = Uint8List.fromList(data);  
167 -  
168 - if (cache) {  
169 - await imageCache.add(url, bytes);  
170 - } 109 + pdfCache ??= PdfBaseCache.defaultCache;
  110 + final bytes = await pdfCache.resolve(
  111 + name: url,
  112 + uri: Uri.parse(url),
  113 + cache: cache,
  114 + headers: headers,
  115 + );
171 116
172 return MemoryImage(bytes, orientation: orientation, dpi: dpi); 117 return MemoryImage(bytes, orientation: orientation, dpi: dpi);
173 } 118 }
  1 +/*
  2 + * Copyright (C) 2017, David PHAM-VAN <dev.nfet.net@gmail.com>
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +import 'dart:async';
  18 +import 'dart:typed_data';
  19 +
  20 +import 'package:flutter/foundation.dart';
  21 +import 'package:http/http.dart' as http;
  22 +
  23 +/// Store data in a cache
  24 +abstract class PdfBaseCache {
  25 + /// Create a cache
  26 + const PdfBaseCache();
  27 +
  28 + /// The default cache used when none specified
  29 + static final defaultCache = PdfMemoryCache();
  30 +
  31 + /// Add some data to the cache
  32 + Future<void> add(String key, Uint8List bytes);
  33 +
  34 + /// Retrieve some data from the cache
  35 + Future<Uint8List?> get(String key);
  36 +
  37 + /// Does the cache contains this data?
  38 + Future<bool> contains(String key);
  39 +
  40 + /// Remove some data from the cache
  41 + Future<void> remove(String key);
  42 +
  43 + /// Clear the cache
  44 + Future<void> clear();
  45 +
  46 + /// Download the font
  47 + Future<Uint8List?> _download(
  48 + Uri uri, {
  49 + Map<String, String>? headers,
  50 + }) async {
  51 + final response = await http.get(uri, headers: headers);
  52 + if (response.statusCode != 200) {
  53 + return null;
  54 + }
  55 +
  56 + return response.bodyBytes;
  57 + }
  58 +
  59 + /// Resolve the data
  60 + Future<Uint8List> resolve({
  61 + required String name,
  62 + required Uri uri,
  63 + bool cache = true,
  64 + Map<String, String>? headers,
  65 + }) async {
  66 + if (cache && await contains(name)) {
  67 + return (await get(name))!;
  68 + }
  69 +
  70 + final bytes = await _download(uri, headers: headers);
  71 +
  72 + if (bytes == null) {
  73 + throw FlutterError('Unable to find the asset $name');
  74 + }
  75 +
  76 + if (cache) {
  77 + await add(name, bytes);
  78 + }
  79 +
  80 + return bytes;
  81 + }
  82 +}
  83 +
  84 +/// Memory cache
  85 +class PdfMemoryCache extends PdfBaseCache {
  86 + /// Create a memory cache
  87 + PdfMemoryCache();
  88 +
  89 + final _imageCache = <String, Uint8List>{};
  90 +
  91 + Timer? _timer;
  92 +
  93 + void _resetTimer() {
  94 + _timer?.cancel();
  95 + _timer = Timer(const Duration(minutes: 20), () {
  96 + clear();
  97 + });
  98 + }
  99 +
  100 + @override
  101 + Future<void> add(String key, Uint8List bytes) async {
  102 + _imageCache[key] = bytes;
  103 + _resetTimer();
  104 + }
  105 +
  106 + @override
  107 + Future<Uint8List?> get(String key) async {
  108 + _resetTimer();
  109 + return _imageCache[key];
  110 + }
  111 +
  112 + @override
  113 + Future<void> clear() async {
  114 + _imageCache.clear();
  115 + }
  116 +
  117 + @override
  118 + Future<bool> contains(String key) async {
  119 + return _imageCache.containsKey(key);
  120 + }
  121 +
  122 + @override
  123 + Future<void> remove(String key) async {
  124 + _imageCache.remove(key);
  125 + }
  126 +}
  127 +
  128 +/// Store network images in a cache
  129 +@Deprecated('Use PdfBaseCache instead')
  130 +abstract class PdfBaseImageCache extends PdfBaseCache {
  131 + /// Create a network image cache
  132 + const PdfBaseImageCache();
  133 +
  134 + /// The default cache used when none specified
  135 + static final defaultCache = PdfBaseCache.defaultCache;
  136 +}
  137 +
  138 +/// Memory image cache
  139 +@Deprecated('Use PdfMemoryCache instead')
  140 +class PdfMemoryImageCache extends PdfMemoryCache {
  141 + /// Create a memory image cache
  142 + PdfMemoryImageCache();
  143 +}
  1 +import 'package:flutter/services.dart';
  2 +import 'package:pdf/widgets.dart';
  3 +
  4 +import '../cache.dart';
  5 +import 'manifest.dart';
  6 +
  7 +/// Downloadable font object
  8 +class DownloadbleFont {
  9 + /// Create a downloadable font object
  10 + const DownloadbleFont(this.url, this.name);
  11 +
  12 + /// The Url to get the font from
  13 + final String url;
  14 +
  15 + /// The Font filename
  16 + final String name;
  17 +
  18 + /// The cache to use
  19 + static var cache = PdfBaseCache.defaultCache;
  20 +
  21 + /// Get the font to use in a Pdf document
  22 + Future<TtfFont> getFont({
  23 + PdfBaseCache? pdfCache,
  24 + bool protect = false,
  25 + Map<String, String>? headers,
  26 + String assetPrefix = 'google_fonts/',
  27 + AssetBundle? bundle,
  28 + bool cache = true,
  29 + }) async {
  30 + final asset = '$assetPrefix$name.ttf';
  31 + if (await AssetManifest.contains(asset)) {
  32 + bundle ??= rootBundle;
  33 + final data = await bundle.load(asset);
  34 + return TtfFont(
  35 + data,
  36 + protect: protect,
  37 + );
  38 + }
  39 +
  40 + pdfCache ??= PdfBaseCache.defaultCache;
  41 + final bytes = await pdfCache.resolve(
  42 + name: name,
  43 + uri: Uri.parse(url),
  44 + headers: headers,
  45 + cache: cache,
  46 + );
  47 +
  48 + return TtfFont(
  49 + bytes.buffer.asByteData(bytes.offsetInBytes, bytes.lengthInBytes),
  50 + protect: protect,
  51 + );
  52 + }
  53 +}
This diff could not be displayed because it is too large.
  1 +import 'dart:async';
  2 +import 'dart:convert';
  3 +
  4 +import 'package:flutter/services.dart';
  5 +
  6 +/// Application asset manifest.
  7 +mixin AssetManifest {
  8 + static final _assets = <String>[];
  9 +
  10 + static final _mutex = Mutex();
  11 +
  12 + static bool _ready = false;
  13 + static bool _failed = false;
  14 +
  15 + /// Does is contains this key?
  16 + static Future<bool> contains(String key) async {
  17 + if (_failed) {
  18 + return false;
  19 + }
  20 +
  21 + await _mutex.acquire();
  22 + try {
  23 + if (!_ready) {
  24 + try {
  25 + final jsonString = await rootBundle.loadString('AssetManifest.json');
  26 + final jsonData = json.decode(jsonString) as Map<String, dynamic>;
  27 + _assets.addAll(jsonData.keys);
  28 + } catch (e) {
  29 + print('Error loading AssetManifest.json $e');
  30 + rootBundle.evict('AssetManifest.json');
  31 + _failed = true;
  32 + _ready = true;
  33 + return false;
  34 + }
  35 + _ready = true;
  36 + }
  37 + } finally {
  38 + _mutex.release();
  39 + }
  40 +
  41 + return _assets.contains(key);
  42 + }
  43 +}
  44 +
  45 +/// Simple Mutex
  46 +class Mutex {
  47 + final _waiting = <Completer>[];
  48 +
  49 + bool _locked = false;
  50 +
  51 + /// Lock the mutex
  52 + Future<void> acquire() async {
  53 + if (_locked) {
  54 + final c = Completer<void>();
  55 + _waiting.add(c);
  56 + await c.future;
  57 + }
  58 + _locked = true;
  59 + }
  60 +
  61 + /// Release the mutex
  62 + void release() {
  63 + _locked = false;
  64 + for (final e in _waiting) {
  65 + e.complete();
  66 + }
  67 + }
  68 +}
@@ -7,7 +7,7 @@ description: > @@ -7,7 +7,7 @@ description: >
7 homepage: https://github.com/DavBfr/dart_pdf/tree/master/printing 7 homepage: https://github.com/DavBfr/dart_pdf/tree/master/printing
8 repository: https://github.com/DavBfr/dart_pdf 8 repository: https://github.com/DavBfr/dart_pdf
9 issue_tracker: https://github.com/DavBfr/dart_pdf/issues 9 issue_tracker: https://github.com/DavBfr/dart_pdf/issues
10 -version: 5.3.0 10 +version: 5.4.0
11 11
12 environment: 12 environment:
13 sdk: ">=2.12.0 <3.0.0" 13 sdk: ">=2.12.0 <3.0.0"
@@ -19,6 +19,7 @@ dependencies: @@ -19,6 +19,7 @@ dependencies:
19 sdk: flutter 19 sdk: flutter
20 flutter_web_plugins: 20 flutter_web_plugins:
21 sdk: flutter 21 sdk: flutter
  22 + http: ^0.13.0
22 image: ^3.0.1 23 image: ^3.0.1
23 js: ^0.6.3 24 js: ^0.6.3
24 meta: ^1.3.0 25 meta: ^1.3.0
  1 +import 'dart:convert';
  2 +import 'dart:io';
  3 +
  4 +String _capitalize(String s) {
  5 + if (s.isEmpty) return s;
  6 + return '${s[0].toUpperCase()}${s.substring(1)}';
  7 +}
  8 +
  9 +String _uncapitalize(String s) {
  10 + if (s.isEmpty) return s;
  11 + return '${s[0].toLowerCase()}${s.substring(1)}';
  12 +}
  13 +
  14 +void main(List<String> args) async {
  15 + final f = File('fonts.json');
  16 + final d = StringBuffer();
  17 +
  18 + if (f.existsSync()) {
  19 + d.write(await f.readAsString());
  20 + } else {
  21 + final key = args[0];
  22 + final http = HttpClient();
  23 + print('Downloading...');
  24 + final q = await http.getUrl(Uri.parse(
  25 + 'https://content-webfonts.googleapis.com/v1/webfonts?key=$key'));
  26 + final r = await q.close();
  27 +
  28 + await for (final c in r.transform(utf8.decoder)) {
  29 + d.write(c);
  30 + }
  31 +
  32 + await f.writeAsString(d.toString());
  33 + }
  34 +
  35 + print('Converting...');
  36 + final Map m = json.decode(d.toString());
  37 +
  38 + final output =
  39 + await File('../printing/lib/src/fonts/gfonts.dart').openWrite();
  40 +
  41 + output.writeln('import \'package:pdf/widgets.dart\';');
  42 + output.writeln('');
  43 + output.writeln('import \'font.dart\';');
  44 + output.writeln('');
  45 + output.writeln('/// Google Fonts');
  46 + output.writeln('class PdfGoogleFonts extends DownloadbleFont {');
  47 + output.writeln('');
  48 + output.writeln('/// Create a Google Font');
  49 + output.writeln(
  50 + 'const PdfGoogleFonts._(String url, String name) : super(url, name);');
  51 +
  52 + for (final f in m['items']) {
  53 + final family = _uncapitalize(f['family'].replaceAll(' ', ''));
  54 +
  55 + for (final s in f['files'].entries) {
  56 + var sub = _capitalize(s.key);
  57 +
  58 + sub = sub.replaceAll('100', 'Thin ');
  59 + sub = sub.replaceAll('200', 'ExtraLight ');
  60 + sub = sub.replaceAll('300', 'Light ');
  61 + sub = sub.replaceAll('400', 'Regular ');
  62 + sub = sub.replaceAll('500', 'Medium ');
  63 + sub = sub.replaceAll('600', 'SemiBold ');
  64 + sub = sub.replaceAll('700', 'Bold ');
  65 + sub = sub.replaceAll('800', 'ExtraBold ');
  66 + sub = sub.replaceAll('900', 'Black ');
  67 + sub = sub.split(' ').map<String>((String e) => _capitalize(e)).join('');
  68 +
  69 + final name = _capitalize(family) + '-' + sub;
  70 +
  71 + output.writeln('');
  72 + output.writeln('/// ${f['family']} ${s.key}');
  73 + output.writeln('static Future<Font> $family$sub() {');
  74 + output.writeln(
  75 + 'const font = PdfGoogleFonts._(\'${s.value}\', \'$name\',);');
  76 + output.writeln('return font.getFont();');
  77 + output.writeln('}');
  78 + }
  79 + }
  80 +
  81 + for (final entry in <String, String>{
  82 + 'MaterialIcons':
  83 + 'https://fonts.gstatic.com/s/materialicons/v90/flUhRq6tzZclQEJ-Vdg-IuiaDsNZ.ttf'
  84 + }.entries) {
  85 + output.writeln('');
  86 + output.writeln('/// ${entry.key}');
  87 + output.writeln('static Future<Font> ${_uncapitalize(entry.key)}() {');
  88 + output.writeln(
  89 + 'const font = PdfGoogleFonts._(\'${entry.value}\', \'${entry.key}\',);');
  90 + output.writeln('return font.getFont();');
  91 + output.writeln('}');
  92 + }
  93 +
  94 + output.writeln('}');
  95 +
  96 + await output.close();
  97 + print('Done');
  98 +}