Committed by
David PHAM-VAN
Add support for custom fonts in SVGs.
This adds the interface `SvgCustomFonts` and a corresponding parameter to the SvgImage widget. This provides a mechanism to support SVGs that have known fonts referenced in them. Closes #1731.
Showing
7 changed files
with
53 additions
and
3 deletions
| @@ -16,6 +16,7 @@ | @@ -16,6 +16,7 @@ | ||
| 16 | 16 | ||
| 17 | import '../../pdf.dart'; | 17 | import '../../pdf.dart'; |
| 18 | import '../widgets/font.dart'; | 18 | import '../widgets/font.dart'; |
| 19 | +import '../widgets/svg.dart'; | ||
| 19 | import 'brush.dart'; | 20 | import 'brush.dart'; |
| 20 | import 'color.dart'; | 21 | import 'color.dart'; |
| 21 | import 'group.dart'; | 22 | import 'group.dart'; |
| @@ -26,8 +27,9 @@ class SvgPainter { | @@ -26,8 +27,9 @@ class SvgPainter { | ||
| 26 | this.parser, | 27 | this.parser, |
| 27 | this._canvas, | 28 | this._canvas, |
| 28 | this.document, | 29 | this.document, |
| 29 | - this.boundingBox, | ||
| 30 | - ); | 30 | + this.boundingBox, { |
| 31 | + this.customFontLookup, | ||
| 32 | + }); | ||
| 31 | 33 | ||
| 32 | final SvgParser parser; | 34 | final SvgParser parser; |
| 33 | 35 | ||
| @@ -37,6 +39,8 @@ class SvgPainter { | @@ -37,6 +39,8 @@ class SvgPainter { | ||
| 37 | 39 | ||
| 38 | final PdfRect boundingBox; | 40 | final PdfRect boundingBox; |
| 39 | 41 | ||
| 42 | + final SvgCustomFontLookup? customFontLookup; | ||
| 43 | + | ||
| 40 | void paint() { | 44 | void paint() { |
| 41 | final brush = parser.colorFilter == null | 45 | final brush = parser.colorFilter == null |
| 42 | ? SvgBrush.defaultContext | 46 | ? SvgBrush.defaultContext |
| @@ -59,6 +63,12 @@ class SvgPainter { | @@ -59,6 +63,12 @@ class SvgPainter { | ||
| 59 | } | 63 | } |
| 60 | 64 | ||
| 61 | Font getFont(String fontFamily, String fontStyle, String fontWeight) { | 65 | Font getFont(String fontFamily, String fontStyle, String fontWeight) { |
| 66 | + final customFont = | ||
| 67 | + customFontLookup?.call(fontFamily, fontStyle, fontWeight); | ||
| 68 | + if (customFont != null) { | ||
| 69 | + return customFont; | ||
| 70 | + } | ||
| 71 | + | ||
| 62 | switch (fontFamily) { | 72 | switch (fontFamily) { |
| 63 | case 'serif': | 73 | case 'serif': |
| 64 | switch (fontStyle) { | 74 | switch (fontStyle) { |
| @@ -32,6 +32,7 @@ class SvgImage extends Widget { | @@ -32,6 +32,7 @@ class SvgImage extends Widget { | ||
| 32 | double? width, | 32 | double? width, |
| 33 | double? height, | 33 | double? height, |
| 34 | PdfColor? colorFilter, | 34 | PdfColor? colorFilter, |
| 35 | + SvgCustomFontLookup? customFontLookup, | ||
| 35 | }) { | 36 | }) { |
| 36 | final xml = XmlDocument.parse(svg); | 37 | final xml = XmlDocument.parse(svg); |
| 37 | final parser = SvgParser( | 38 | final parser = SvgParser( |
| @@ -46,6 +47,7 @@ class SvgImage extends Widget { | @@ -46,6 +47,7 @@ class SvgImage extends Widget { | ||
| 46 | clip, | 47 | clip, |
| 47 | width, | 48 | width, |
| 48 | height, | 49 | height, |
| 50 | + customFontLookup, | ||
| 49 | ); | 51 | ); |
| 50 | } | 52 | } |
| 51 | 53 | ||
| @@ -56,6 +58,7 @@ class SvgImage extends Widget { | @@ -56,6 +58,7 @@ class SvgImage extends Widget { | ||
| 56 | this.clip, | 58 | this.clip, |
| 57 | this.width, | 59 | this.width, |
| 58 | this.height, | 60 | this.height, |
| 61 | + this.customFontLookup, | ||
| 59 | ); | 62 | ); |
| 60 | 63 | ||
| 61 | final SvgParser _svgParser; | 64 | final SvgParser _svgParser; |
| @@ -70,6 +73,8 @@ class SvgImage extends Widget { | @@ -70,6 +73,8 @@ class SvgImage extends Widget { | ||
| 70 | 73 | ||
| 71 | final double? height; | 74 | final double? height; |
| 72 | 75 | ||
| 76 | + final SvgCustomFontLookup? customFontLookup; | ||
| 77 | + | ||
| 73 | late FittedSizes sizes; | 78 | late FittedSizes sizes; |
| 74 | 79 | ||
| 75 | @override | 80 | @override |
| @@ -126,6 +131,7 @@ class SvgImage extends Widget { | @@ -126,6 +131,7 @@ class SvgImage extends Widget { | ||
| 126 | context.page.pageFormat.width, | 131 | context.page.pageFormat.width, |
| 127 | context.page.pageFormat.height, | 132 | context.page.pageFormat.height, |
| 128 | ), | 133 | ), |
| 134 | + customFontLookup: customFontLookup, | ||
| 129 | ); | 135 | ); |
| 130 | painter.paint(); | 136 | painter.paint(); |
| 131 | context.canvas.restoreContext(); | 137 | context.canvas.restoreContext(); |
| @@ -154,3 +160,6 @@ class DecorationSvgImage extends DecorationGraphic { | @@ -154,3 +160,6 @@ class DecorationSvgImage extends DecorationGraphic { | ||
| 154 | ); | 160 | ); |
| 155 | } | 161 | } |
| 156 | } | 162 | } |
| 163 | + | ||
| 164 | +typedef SvgCustomFontLookup = Font? Function( | ||
| 165 | + String fontFamily, String fontStyle, String fontWeight); |
| @@ -20,6 +20,8 @@ import 'package:pdf/pdf.dart'; | @@ -20,6 +20,8 @@ import 'package:pdf/pdf.dart'; | ||
| 20 | import 'package:pdf/widgets.dart'; | 20 | import 'package:pdf/widgets.dart'; |
| 21 | import 'package:test/test.dart'; | 21 | import 'package:test/test.dart'; |
| 22 | 22 | ||
| 23 | +import 'utils.dart'; | ||
| 24 | + | ||
| 23 | late Document pdf; | 25 | late Document pdf; |
| 24 | 26 | ||
| 25 | void main() { | 27 | void main() { |
| @@ -195,6 +197,31 @@ void main() { | @@ -195,6 +197,31 @@ void main() { | ||
| 195 | ); | 197 | ); |
| 196 | }); | 198 | }); |
| 197 | 199 | ||
| 200 | + test('SVG Widgets Text custom fonts', () { | ||
| 201 | + const customFamilyName1 = 'Test-Font-Family1'; | ||
| 202 | + const customFamilyName2 = 'Test-Font-Family2'; | ||
| 203 | + final customFont1 = loadFont('open-sans-bold.ttf'); | ||
| 204 | + final customFont2 = loadFont('genyomintw.ttf'); | ||
| 205 | + pdf.addPage( | ||
| 206 | + Page( | ||
| 207 | + build: (context) => SvgImage( | ||
| 208 | + svg: | ||
| 209 | + '<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg viewBox="0 0 1000 300" xmlns="http://www.w3.org/2000/svg" version="1.1"><text x="367.055" y="168.954" font-size="55" fill="darkgreen" font-family="$customFamilyName1" >Custom <tspan font-family="$customFamilyName2">fonts</tspan></text><rect x="1" y="1" width="998" height="298" fill="none" stroke="purple" stroke-width="2" /></svg>', | ||
| 210 | + customFontLookup: | ||
| 211 | + (String fontFamily, String fontStyle, String fontWeight) { | ||
| 212 | + switch (fontFamily) { | ||
| 213 | + case customFamilyName1: | ||
| 214 | + return customFont1; | ||
| 215 | + case customFamilyName2: | ||
| 216 | + return customFont2; | ||
| 217 | + default: | ||
| 218 | + return null; | ||
| 219 | + } | ||
| 220 | + }), | ||
| 221 | + ), | ||
| 222 | + ); | ||
| 223 | + }); | ||
| 224 | + | ||
| 198 | tearDownAll(() async { | 225 | tearDownAll(() async { |
| 199 | final file = File('widgets-svg.pdf'); | 226 | final file = File('widgets-svg.pdf'); |
| 200 | await file.writeAsBytes(await pdf.save()); | 227 | await file.writeAsBytes(await pdf.save()); |
No preview for this file type
No preview for this file type
-
Please register or login to post a comment