├── .github
└── workflows
│ └── build.yml
├── .gitignore
├── .rspec
├── .rubocop.yml
├── Dockerfile
├── Gemfile
├── Gemfile.lock
├── README.md
├── Rakefile
├── bin
├── console
└── setup
├── docker-compose.yml
├── examples
├── api_keys.rb
├── audiences.rb
├── batch_email.rb
├── broadcasts.rb
├── contacts.rb
├── domains.rb
├── schedule_email.rb
├── simple_mail.rb
├── with_attachments.rb
└── with_b64_attachments.rb
├── lib
├── resend.rb
└── resend
│ ├── api_keys.rb
│ ├── audiences.rb
│ ├── batch.rb
│ ├── broadcasts.rb
│ ├── client.rb
│ ├── contacts.rb
│ ├── domains.rb
│ ├── emails.rb
│ ├── errors.rb
│ ├── mailer.rb
│ ├── railtie.rb
│ ├── request.rb
│ └── version.rb
├── renovate.json
├── resend.gemspec
├── resources
└── invoice.pdf
├── sig
└── resend.rbs
└── spec
├── api_keys_spec.rb
├── audiences_spec.rb
├── batch_spec.rb
├── broadcasts_spec.rb
├── contacts_spec.rb
├── domains_spec.rb
├── emails_spec.rb
├── railtie
└── mailer_spec.rb
├── request_spec.rb
├── resend_spec.rb
└── spec_helper.rb
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on: ["push", "pull_request"]
4 |
5 | jobs:
6 | test:
7 | name: Ruby ${{ matrix.ruby }}
8 | runs-on: ubuntu-latest
9 | strategy:
10 | fail-fast: false # don't fail all matrix builds if one fails
11 | matrix:
12 | ruby:
13 | - "2.7"
14 | - "3.0"
15 | - "3.1"
16 | continue-on-error: ${{ endsWith(matrix.ruby, 'head') }}
17 | steps:
18 | - uses: actions/checkout@v4
19 | - uses: ruby/setup-ruby@v1
20 | with:
21 | ruby-version: ${{ matrix.ruby }}
22 | - name: Install dependencies
23 | run: bundle install
24 | - name: Rubocop
25 | run: bundle exec rubocop
26 | - name: Run tests
27 | run: bundle exec rake spec
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.bundle/
2 | /.yardoc
3 | /_yardoc/
4 | /coverage/
5 | /doc/
6 | /pkg/
7 | /spec/reports/
8 | /tmp/
9 | /sig/
10 |
11 | # rspec failure tracking
12 | .rspec_status
13 |
14 | test.rb
15 | *.gem
16 | .vscode
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --format documentation
2 | --color
3 | --require spec_helper
4 |
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | AllCops:
2 | TargetRubyVersion: 2.6
3 | SuggestExtensions: false
4 | NewCops: disable
5 | Exclude:
6 | - "./spec/**/**"
7 | - "./examples/**"
8 |
9 | Style/GuardClause:
10 | Enabled: false
11 |
12 | Style/StringLiterals:
13 | Enabled: true
14 | EnforcedStyle: double_quotes
15 |
16 | Style/StringLiteralsInInterpolation:
17 | Enabled: true
18 | EnforcedStyle: double_quotes
19 |
20 | Layout/LineLength:
21 | Max: 120
22 |
23 | Metrics/ClassLength:
24 | Max: 169
25 |
26 | Metrics/MethodLength:
27 | Max: 14
28 |
29 | Metrics/ParameterLists:
30 | MaxOptionalParameters: 4
31 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ruby:3.4.3
2 | RUN apt-get update -qq && apt-get install -y build-essential libpq-dev
3 |
4 | RUN mkdir -p /app
5 |
6 | WORKDIR /app
7 |
8 | ADD . /app/
9 |
10 | RUN bundle install
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | source "https://rubygems.org"
4 |
5 | # Specify your gem's dependencies in resend.gemspec
6 | gemspec
7 |
8 | gem "rake", "~> 13.0"
9 |
10 | gem "rspec", "~> 3.0"
11 |
12 | gem "rubocop", "~> 1.21"
13 |
14 | gem "httparty", "~> 0.21.0"
15 |
16 | gem "pry-byebug"
17 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: .
3 | specs:
4 | resend (0.22.0)
5 | httparty (>= 0.21.0)
6 |
7 | GEM
8 | remote: https://rubygems.org/
9 | specs:
10 | actioncable (7.0.4)
11 | actionpack (= 7.0.4)
12 | activesupport (= 7.0.4)
13 | nio4r (~> 2.0)
14 | websocket-driver (>= 0.6.1)
15 | actionmailbox (7.0.4)
16 | actionpack (= 7.0.4)
17 | activejob (= 7.0.4)
18 | activerecord (= 7.0.4)
19 | activestorage (= 7.0.4)
20 | activesupport (= 7.0.4)
21 | mail (>= 2.7.1)
22 | net-imap
23 | net-pop
24 | net-smtp
25 | actionmailer (7.0.4)
26 | actionpack (= 7.0.4)
27 | actionview (= 7.0.4)
28 | activejob (= 7.0.4)
29 | activesupport (= 7.0.4)
30 | mail (~> 2.5, >= 2.5.4)
31 | net-imap
32 | net-pop
33 | net-smtp
34 | rails-dom-testing (~> 2.0)
35 | actionpack (7.0.4)
36 | actionview (= 7.0.4)
37 | activesupport (= 7.0.4)
38 | rack (~> 2.0, >= 2.2.0)
39 | rack-test (>= 0.6.3)
40 | rails-dom-testing (~> 2.0)
41 | rails-html-sanitizer (~> 1.0, >= 1.2.0)
42 | actiontext (7.0.4)
43 | actionpack (= 7.0.4)
44 | activerecord (= 7.0.4)
45 | activestorage (= 7.0.4)
46 | activesupport (= 7.0.4)
47 | globalid (>= 0.6.0)
48 | nokogiri (>= 1.8.5)
49 | actionview (7.0.4)
50 | activesupport (= 7.0.4)
51 | builder (~> 3.1)
52 | erubi (~> 1.4)
53 | rails-dom-testing (~> 2.0)
54 | rails-html-sanitizer (~> 1.1, >= 1.2.0)
55 | activejob (7.0.4)
56 | activesupport (= 7.0.4)
57 | globalid (>= 0.3.6)
58 | activemodel (7.0.4)
59 | activesupport (= 7.0.4)
60 | activerecord (7.0.4)
61 | activemodel (= 7.0.4)
62 | activesupport (= 7.0.4)
63 | activestorage (7.0.4)
64 | actionpack (= 7.0.4)
65 | activejob (= 7.0.4)
66 | activerecord (= 7.0.4)
67 | activesupport (= 7.0.4)
68 | marcel (~> 1.0)
69 | mini_mime (>= 1.1.0)
70 | activesupport (7.0.4)
71 | concurrent-ruby (~> 1.0, >= 1.0.2)
72 | i18n (>= 1.6, < 2)
73 | minitest (>= 5.1)
74 | tzinfo (~> 2.0)
75 | ast (2.4.2)
76 | builder (3.2.4)
77 | byebug (11.1.3)
78 | coderay (1.1.3)
79 | concurrent-ruby (1.1.10)
80 | crass (1.0.6)
81 | date (3.3.3)
82 | diff-lcs (1.5.1)
83 | erubi (1.12.0)
84 | globalid (1.0.0)
85 | activesupport (>= 5.0)
86 | httparty (0.21.0)
87 | mini_mime (>= 1.0.0)
88 | multi_xml (>= 0.5.2)
89 | i18n (1.12.0)
90 | concurrent-ruby (~> 1.0)
91 | json (2.10.2)
92 | language_server-protocol (3.17.0.4)
93 | lint_roller (1.1.0)
94 | loofah (2.19.1)
95 | crass (~> 1.0.2)
96 | nokogiri (>= 1.5.9)
97 | mail (2.8.0)
98 | mini_mime (>= 0.1.1)
99 | net-imap
100 | net-pop
101 | net-smtp
102 | marcel (1.0.2)
103 | method_source (1.0.0)
104 | mini_mime (1.1.2)
105 | mini_portile2 (2.8.1)
106 | minitest (5.16.3)
107 | multi_xml (0.6.0)
108 | net-imap (0.3.4)
109 | date
110 | net-protocol
111 | net-pop (0.1.2)
112 | net-protocol
113 | net-protocol (0.2.1)
114 | timeout
115 | net-smtp (0.3.3)
116 | net-protocol
117 | nio4r (2.5.8)
118 | nokogiri (1.13.10)
119 | mini_portile2 (~> 2.8.0)
120 | racc (~> 1.4)
121 | parallel (1.26.3)
122 | parser (3.3.7.1)
123 | ast (~> 2.4.1)
124 | racc
125 | pry (0.14.2)
126 | coderay (~> 1.1)
127 | method_source (~> 1.0)
128 | pry-byebug (3.10.1)
129 | byebug (~> 11.0)
130 | pry (>= 0.13, < 0.15)
131 | racc (1.8.1)
132 | rack (2.2.5)
133 | rack-test (2.0.2)
134 | rack (>= 1.3)
135 | rails (7.0.4)
136 | actioncable (= 7.0.4)
137 | actionmailbox (= 7.0.4)
138 | actionmailer (= 7.0.4)
139 | actionpack (= 7.0.4)
140 | actiontext (= 7.0.4)
141 | actionview (= 7.0.4)
142 | activejob (= 7.0.4)
143 | activemodel (= 7.0.4)
144 | activerecord (= 7.0.4)
145 | activestorage (= 7.0.4)
146 | activesupport (= 7.0.4)
147 | bundler (>= 1.15.0)
148 | railties (= 7.0.4)
149 | rails-dom-testing (2.0.3)
150 | activesupport (>= 4.2.0)
151 | nokogiri (>= 1.6)
152 | rails-html-sanitizer (1.4.4)
153 | loofah (~> 2.19, >= 2.19.1)
154 | railties (7.0.4)
155 | actionpack (= 7.0.4)
156 | activesupport (= 7.0.4)
157 | method_source
158 | rake (>= 12.2)
159 | thor (~> 1.0)
160 | zeitwerk (~> 2.5)
161 | rainbow (3.1.1)
162 | rake (13.2.1)
163 | regexp_parser (2.10.0)
164 | rspec (3.13.1)
165 | rspec-core (~> 3.13.0)
166 | rspec-expectations (~> 3.13.0)
167 | rspec-mocks (~> 3.13.0)
168 | rspec-core (3.13.4)
169 | rspec-support (~> 3.13.0)
170 | rspec-expectations (3.13.5)
171 | diff-lcs (>= 1.2.0, < 2.0)
172 | rspec-support (~> 3.13.0)
173 | rspec-mocks (3.13.5)
174 | diff-lcs (>= 1.2.0, < 2.0)
175 | rspec-support (~> 3.13.0)
176 | rspec-support (3.13.4)
177 | rubocop (1.74.0)
178 | json (~> 2.3)
179 | language_server-protocol (~> 3.17.0.2)
180 | lint_roller (~> 1.1.0)
181 | parallel (~> 1.10)
182 | parser (>= 3.3.0.2)
183 | rainbow (>= 2.2.2, < 4.0)
184 | regexp_parser (>= 2.9.3, < 3.0)
185 | rubocop-ast (>= 1.38.0, < 2.0)
186 | ruby-progressbar (~> 1.7)
187 | unicode-display_width (>= 2.4.0, < 4.0)
188 | rubocop-ast (1.38.1)
189 | parser (>= 3.3.1.0)
190 | ruby-progressbar (1.13.0)
191 | thor (1.2.1)
192 | timeout (0.3.1)
193 | tzinfo (2.0.5)
194 | concurrent-ruby (~> 1.0)
195 | unicode-display_width (2.6.0)
196 | websocket-driver (0.7.5)
197 | websocket-extensions (>= 0.1.0)
198 | websocket-extensions (0.1.5)
199 | zeitwerk (2.6.6)
200 |
201 | PLATFORMS
202 | aarch64-linux
203 | arm64-darwin-22
204 | x86_64-darwin-20
205 | x86_64-darwin-21
206 | x86_64-linux
207 |
208 | DEPENDENCIES
209 | httparty (~> 0.21.0)
210 | pry-byebug
211 | rails
212 | rake (~> 13.0)
213 | resend!
214 | rspec (~> 3.0)
215 | rubocop (~> 1.21)
216 |
217 | BUNDLED WITH
218 | 2.3.11
219 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Resend Ruby and Rails SDK
2 |
3 | [](https://opensource.org/licenses/MIT)
4 | 
5 | [](https://badge.fury.io/rb/resend)
6 |
7 | ---
8 |
9 | ## Installation
10 |
11 | To install Resend Ruby and Rails SDK, simply execute the following command in a terminal:
12 |
13 | Via RubyGems:
14 |
15 | ```
16 | gem install resend
17 | ```
18 |
19 | Via Gemfile:
20 |
21 | ```
22 | gem 'resend'
23 | ```
24 |
25 | ## Setup
26 |
27 | First, you need to get an API key, which is available in the [Resend Dashboard](https://resend.com).
28 |
29 | ```ruby
30 | require "resend"
31 | Resend.api_key = ENV["RESEND_API_KEY"]
32 | ```
33 |
34 | or
35 |
36 | ```ruby
37 | require "resend"
38 | Resend.configure do |config|
39 | config.api_key = ENV["RESEND_API_KEY"]
40 | end
41 | ```
42 |
43 | The `#api_key` method also accepts a block without arguments, which can be useful for lazy or dynamic loading of API keys.
44 |
45 | ```ruby
46 | require "resend"
47 | Resend.api_key = -> { ENV["RESEND_API_KEY"] }
48 | ```
49 |
50 | ```ruby
51 | Resend.configure do |config|
52 | config.api_key = -> { Current.user.resend_api_key } # Assumes the user has a `resend_api_key` attribute.
53 | end
54 | ```
55 |
56 | ## Example
57 |
58 | ```rb
59 | require "resend"
60 |
61 | Resend.api_key = ENV["RESEND_API_KEY"]
62 |
63 | params = {
64 | "from": "from@email.io",
65 | "to": ["to@email.com", "to1@gmail.com"],
66 | "html": "
Hello World
",
67 | "subject": "Hey"
68 | }
69 | r = Resend::Emails.send(params)
70 | puts r
71 | ```
72 |
73 | You can view all the examples in the [examples folder](https://github.com/drish/resend-ruby/tree/main/examples)
74 |
75 | # Rails and ActiveMailer support
76 |
77 | This gem can be used as an ActionMailer delivery method, add this to your `config/environments/environment.rb` file.
78 |
79 | ```ruby
80 | config.action_mailer.delivery_method = :resend
81 | ```
82 |
83 | Create or update your mailer initializer file and replace the placeholder with your Resend API Key.
84 |
85 | ```rb
86 | # /config/initializers/resend.rb
87 | Resend.api_key = "re_123456"
88 | ```
89 |
90 | After that you can deliver_now!, example below:
91 |
92 | ```ruby
93 | #/app/mailers/user_mailer
94 | class UserMailer < ApplicationMailer
95 | default from: 'you@yourdomain.io'
96 | def welcome_email
97 | @user = params[:user]
98 | @url = 'http://example.com/login'
99 | mail(to: ["example2@mail.com", "example1@mail.com"], subject: 'Hello from Resend')
100 | end
101 | end
102 |
103 | # anywhere in the app
104 | u = User.new name: "derich"
105 | mailer = UserMailer.with(user: u).welcome_email
106 | mailer.deliver_now!
107 | # => {:id=>"b8f94710-0d84-429c-925a-22d3d8f86916", from: 'you@yourdomain.io', to: ["example2@mail.com", "example1@mail.com"]}
108 | ```
109 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "bundler/gem_tasks"
4 | require "rspec/core/rake_task"
5 |
6 | RSpec::Core::RakeTask.new(:spec)
7 |
8 | require "rubocop/rake_task"
9 |
10 | RuboCop::RakeTask.new
11 |
12 | task default: %i[spec rubocop]
13 |
--------------------------------------------------------------------------------
/bin/console:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | require "bundler/setup"
5 | require "resend"
6 |
7 | # You can add fixtures and/or initialization code here to make experimenting
8 | # with your gem easier. You can also use a different console, if you like.
9 |
10 | # (If you use this, don't forget to add pry to your Gemfile!)
11 | # require "pry"
12 | # Pry.start
13 |
14 | require "irb"
15 | IRB.start(__FILE__)
16 |
--------------------------------------------------------------------------------
/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 | IFS=$'\n\t'
4 | set -vx
5 |
6 | bundle install
7 |
8 | # Do any other automated setup that you need to do here
9 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 |
3 | services:
4 | app:
5 | build: .
6 | command: sleep infinity
7 | volumes:
8 | - .:/app
9 | environment:
10 | RESEND_API_KEY: ${RESEND_API_KEY}
--------------------------------------------------------------------------------
/examples/api_keys.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative "../lib/resend"
4 |
5 | raise if ENV["RESEND_API_KEY"].nil?
6 |
7 | Resend.api_key = ENV["RESEND_API_KEY"]
8 |
9 | def create
10 | params = {
11 | name: "name"
12 | }
13 | key = Resend::ApiKeys.create(params)
14 | puts key
15 | end
16 |
17 | def list
18 | keys = Resend::ApiKeys.list
19 | puts keys
20 | end
21 |
22 | def remove
23 | key = Resend::ApiKeys.create({name: "t"})
24 | puts "created api key id: #{key[:id]}"
25 | Resend::ApiKeys.remove key[:id]
26 | puts "removed #{key[:id]}"
27 | end
28 |
29 | create
30 | # list
31 | # remove
32 |
--------------------------------------------------------------------------------
/examples/audiences.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative "../lib/resend"
4 |
5 | raise if ENV["RESEND_API_KEY"].nil?
6 |
7 | Resend.api_key = ENV["RESEND_API_KEY"]
8 |
9 | def example
10 | params = {
11 | name: "New Audience One",
12 | }
13 | audience = Resend::Audiences.create(params)
14 | puts "Created new audience: #{audience[:id]}"
15 |
16 | Resend::Audiences.get(audience[:id])
17 | puts "retrieved audience id: #{audience[:id]}"
18 |
19 | audiences = Resend::Audiences.list
20 | puts audiences
21 |
22 | Resend::Audiences.remove audience[:id]
23 | puts "deleted #{audience[:id]}"
24 | end
25 |
26 | example
--------------------------------------------------------------------------------
/examples/batch_email.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative "../lib/resend"
4 |
5 | raise if ENV["RESEND_API_KEY"].nil?
6 |
7 | Resend.api_key = ENV["RESEND_API_KEY"]
8 |
9 | params = [
10 | {
11 | "from": "onboarding@resend.dev",
12 | "to": ["delivered@resend.dev"],
13 | "subject": "hey",
14 | "html": "hello, world!",
15 | },
16 | {
17 | "from": "onboarding@resend.dev",
18 | "to": ["delivered@resend.dev"],
19 | "subject": "hello",
20 | "html": "hello, world!",
21 | },
22 | ]
23 |
24 | # Send a batch of emails without Idempotency key
25 | emails = Resend::Batch.send(params)
26 | puts "Emails sent without Idempotency key:"
27 | puts(emails)
28 |
29 | # Send a batch of emails with Idempotency key
30 | emails = Resend::Batch.send(params, options: { idempotency_key: "af67ff1cdf3cdf1" })
31 | puts "Emails sent with Idempotency key:"
32 | puts(emails)
--------------------------------------------------------------------------------
/examples/broadcasts.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative "../lib/resend"
4 |
5 | raise if ENV["RESEND_API_KEY"].nil?
6 |
7 | Resend.api_key = ENV["RESEND_API_KEY"]
8 |
9 | # replace with an existing audience id
10 | audience_id = "78b8d3bc-a55a-45a3-aee6-6ec0a5e13d7e"
11 |
12 | create_params = {
13 | from: "onboarding@resend.dev",
14 | subject: "Hello from Ruby SDK",
15 | audience_id: audience_id,
16 | text: "Hello, how are you?",
17 | name: "Hello from Ruby SDK",
18 | }
19 |
20 | broadcast = Resend::Broadcasts.create(create_params)
21 | puts "created broadcast: #{broadcast[:id]}"
22 |
23 | update_params = {
24 | broadcast_id: broadcast[:id],
25 | name: "Hello from Ruby SDK - updated",
26 | }
27 |
28 | updated_broadcast = Resend::Broadcasts.update(update_params)
29 | puts "updated broadcast: #{updated_broadcast[:id]}"
30 |
31 | send_params = {
32 | broadcast_id: broadcast[:id],
33 | scheduled_at: "in 1 min",
34 | }
35 |
36 | sent_broadcast = Resend::Broadcasts.send(send_params)
37 | puts "sent broadcast: #{sent_broadcast[:id]}"
38 |
39 | broadcasts = Resend::Broadcasts.list
40 | puts broadcasts
41 |
42 | retrieved = Resend::Broadcasts.get(broadcast[:id])
43 | puts "retrieved #{retrieved[:id]}"
44 |
45 | if retrieved[:status] == 'draft'
46 | Resend::Broadcasts.remove(broadcast[:id])
47 | puts "removed #{broadcast[:id]}"
48 | else
49 | puts 'Cannot remove a broadcast that is not in draft status'
50 | end
--------------------------------------------------------------------------------
/examples/contacts.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative "../lib/resend"
4 |
5 | raise if ENV["RESEND_API_KEY"].nil?
6 |
7 | Resend.api_key = ENV["RESEND_API_KEY"]
8 |
9 | def example
10 |
11 | audience_id = "ca4e37c5-a82a-4199-a3b8-bf912a6472aa"
12 |
13 | params = {
14 | audience_id: audience_id,
15 | email: "steve@example.com",
16 | first_name: "Steve",
17 | last_name: "Woz",
18 | unsubscribed: false,
19 | }
20 |
21 | contact = Resend::Contacts.create(params)
22 | puts "Contact created: #{contact}"
23 |
24 | update_params = {
25 | audience_id: audience_id,
26 | email: params[:email],
27 | # id: contact[:id],
28 | unsubscribed: false,
29 | first_name: "Updated",
30 | }
31 |
32 | retrieved = Resend::Contacts.get(audience_id, contact[:id])
33 | puts "Retrived contact by ID"
34 | puts retrieved
35 |
36 | retrieved_by_email = Resend::Contacts.get(audience_id, contact[:email])
37 | puts "Retrived contact by Email"
38 | puts retrieved_by_email
39 |
40 | updated = Resend::Contacts.update(update_params)
41 | puts "Updated contact: #{updated}"
42 |
43 | contacts = Resend::Contacts.list(audience_id)
44 | puts contacts
45 |
46 | # delete by id
47 | del = Resend::Contacts.remove(audience_id, contact[:id])
48 |
49 | # delete by email
50 | # del = Resend::Contacts.remove(audience_id, "steve@example.com")
51 |
52 | puts "Deleted #{del}"
53 | end
54 |
55 | example
--------------------------------------------------------------------------------
/examples/domains.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative "../lib/resend"
4 |
5 | raise if ENV["RESEND_API_KEY"].nil?
6 |
7 | Resend.api_key = ENV["RESEND_API_KEY"]
8 |
9 | def create
10 | params = {
11 | name: "name",
12 | region: ""
13 | }
14 | domain = Resend::Domains.create(params)
15 | puts domain
16 |
17 | Resend::Domains.remove domain[:id]
18 | puts "deleted #{domain[:id]}"
19 | end
20 |
21 | def get
22 | domain = Resend::Domains.create({name: "test"})
23 | puts "created domain id: #{domain[:id]}"
24 |
25 | retrieved = Resend::Domains.get domain[:id]
26 | puts "retrieved domain: #{retrieved[:id]}"
27 | puts retrieved
28 |
29 | Resend::Domains.remove retrieved[:id]
30 | puts "deleted #{retrieved[:id]}"
31 | end
32 |
33 | def update
34 | domain = Resend::Domains.create({name: "test"})
35 | puts "created domain id: #{domain[:id]}"
36 |
37 | params = {
38 | id: domain[:id],
39 | open_tracking: false,
40 | click_tracking: false,
41 | tls: "enforced"
42 | }
43 | updated = Resend::Domains.update(params)
44 | puts "updated domain: #{updated[:id]}"
45 | puts updated
46 | end
47 |
48 | def list
49 | domains = Resend::Domains.list
50 | puts domains
51 | end
52 |
53 | def remove
54 | domain = Resend::Domains.create({name: "test"})
55 | puts "created domain id: #{domain[:id]}"
56 | Resend::Domains.remove domain[:id]
57 | puts "deleted #{domain[:id]}"
58 | end
59 |
60 | def verify
61 | domain = Resend::Domains.create({name: "test2"})
62 | puts "created domain id: #{domain[:id]}"
63 |
64 | Resend::Domains.verify domain[:id]
65 | puts "verified #{domain[:id]}"
66 |
67 | Resend::Domains.remove domain[:id]
68 | puts "deleted #{domain[:id]}"
69 | end
70 |
71 | create
72 | get
73 | update
74 | list
75 | remove
76 | verify
77 |
--------------------------------------------------------------------------------
/examples/schedule_email.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative "../lib/resend"
4 |
5 | raise if ENV["RESEND_API_KEY"].nil?
6 |
7 | Resend.api_key = ENV["RESEND_API_KEY"]
8 |
9 | params = {
10 | "from": "onboarding@resend.dev",
11 | "to": ["delivered@resend.dev"],
12 | "subject": "Hello from Resend (Ruby SDK) - Scheduled",
13 | "html": "Hello, from Resend (Ruby SDK)",
14 |
15 | # ISO 8601 format, you can get that in Ruby with
16 | # DateTime.now.iso8601(3)
17 | "scheduled_at": "2024-09-05T11:52:01.858Z",
18 | }
19 |
20 | email = Resend::Emails.send(params)
21 | puts(email)
22 |
23 | update_params = {
24 | "email_id": email[:id],
25 | "scheduled_at": "2024-11-05T11:52:01.858Z",
26 | }
27 |
28 | updated_email = Resend::Emails.update(update_params)
29 | puts(updated_email)
30 |
31 | canceled = Resend::Emails.cancel(updated_email[:id])
32 | puts(canceled)
--------------------------------------------------------------------------------
/examples/simple_mail.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative "../lib/resend"
4 |
5 | raise if ENV["RESEND_API_KEY"].nil?
6 |
7 | Resend.api_key = ENV["RESEND_API_KEY"]
8 |
9 | params = {
10 | "from": "onboarding@resend.dev",
11 | "to": ["delivered@resend.dev"],
12 | "subject": "Hello from Resend Ruby SDK",
13 | "text": "test",
14 | "tags": {
15 | "country": "br"
16 | }
17 | }
18 |
19 | # With Idempotency
20 | puts "Sending email with Idempotency key:"
21 | email = Resend::Emails.send(params, options: { idempotency_key: "123" })
22 | puts(email)
23 |
24 | # Without Idempotency
25 | puts "Sending email without Idempotency key:"
26 | email = Resend::Emails.send(params)
27 | puts(email)
28 |
29 | email = Resend::Emails.get(email[:id])
30 | puts(email[:id])
--------------------------------------------------------------------------------
/examples/with_attachments.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative "../lib/resend"
4 | require "stringio"
5 |
6 | raise if ENV["RESEND_API_KEY"].nil?
7 |
8 | Resend.api_key = ENV["RESEND_API_KEY"]
9 |
10 | file = IO.read(File.join(File.dirname(__FILE__), "../resources/invoice.pdf"))
11 |
12 | params = {
13 | "from": "from@email.io",
14 | "to": ["to@hi.com"],
15 | "text": "heyo",
16 | "subject": "Hello with attachment",
17 | "attachments": [
18 | {
19 | "filename": "invoice.pdf", # local file
20 | "content": file.bytes # make sure to use .bytes() here
21 | },
22 | {
23 | "path": "https://github.com/resendlabs/resend-go/raw/main/resources/invoice.pdf",
24 | "filename": "from_external_path.pdf"
25 | }
26 | ]
27 | }
28 |
29 | sent = Resend::Emails.send(params)
30 | puts sent
31 |
--------------------------------------------------------------------------------
/examples/with_b64_attachments.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative "../lib/resend"
4 | require "stringio"
5 | require "base64"
6 |
7 | raise if ENV["RESEND_API_KEY"].nil?
8 |
9 | Resend.api_key = ENV["RESEND_API_KEY"]
10 |
11 | file = IO.read(File.join(File.dirname(__FILE__), "../resources/invoice.pdf"))
12 |
13 | params = {
14 | "from": "onboarding@resend.dev",
15 | "to": ["delivered@resend.dev"],
16 | "text": "heyo",
17 | "subject": "Hello with base 64 encoded attachment",
18 | "attachments": [
19 | {
20 | "filename": "invoice.pdf",
21 | "content": Base64.encode64(file).to_s
22 | },
23 | ]
24 | }
25 |
26 | sent = Resend::Emails.send(params)
27 | puts sent
28 |
--------------------------------------------------------------------------------
/lib/resend.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Version
4 | require "resend/version"
5 |
6 | # Utils
7 | require "httparty"
8 | require "json"
9 | require "resend/errors"
10 | require "resend/client"
11 | require "resend/request"
12 |
13 | # API Operations
14 | require "resend/audiences"
15 | require "resend/api_keys"
16 | require "resend/broadcasts"
17 | require "resend/batch"
18 | require "resend/contacts"
19 | require "resend/domains"
20 | require "resend/emails"
21 |
22 | # Rails
23 | require "resend/railtie" if defined?(Rails) && defined?(ActionMailer)
24 |
25 | # Main Resend module
26 | module Resend
27 | class << self
28 | attr_accessor :api_key
29 |
30 | def configure
31 | yield self if block_given?
32 | true
33 | end
34 | alias config configure
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/lib/resend/api_keys.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Resend
4 | # api keys api wrapper
5 | module ApiKeys
6 | class << self
7 | # https://resend.com/docs/api-reference/api-keys/create-api-key
8 | def create(params)
9 | path = "api-keys"
10 | Resend::Request.new(path, params, "post").perform
11 | end
12 |
13 | # https://resend.com/docs/api-reference/api-keys/list-api-keys
14 | def list
15 | path = "api-keys"
16 | Resend::Request.new(path, {}, "get").perform
17 | end
18 |
19 | # https://resend.com/docs/api-reference/api-keys/delete-api-key
20 | def remove(api_key_id = "")
21 | path = "api-keys/#{api_key_id}"
22 | Resend::Request.new(path, {}, "delete").perform
23 | end
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/lib/resend/audiences.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Resend
4 | # Audiences api wrapper
5 | module Audiences
6 | class << self
7 | # https://resend.com/docs/api-reference/audiences/create-audience
8 | def create(params)
9 | path = "audiences"
10 | Resend::Request.new(path, params, "post").perform
11 | end
12 |
13 | # https://resend.com/docs/api-reference/audiences/get-audience
14 | def get(audience_id = "")
15 | path = "audiences/#{audience_id}"
16 | Resend::Request.new(path, {}, "get").perform
17 | end
18 |
19 | # https://resend.com/docs/api-reference/audiences/list-audiences
20 | def list
21 | path = "audiences"
22 | Resend::Request.new(path, {}, "get").perform
23 | end
24 |
25 | # https://resend.com/docs/api-reference/audiences/delete-audience
26 | def remove(audience_id = "")
27 | path = "audiences/#{audience_id}"
28 | Resend::Request.new(path, {}, "delete").perform
29 | end
30 | end
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/lib/resend/batch.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Resend
4 | # Module responsible for wrapping Batch email sending API
5 | module Batch
6 | class << self
7 | # https://resend.com/docs/api-reference/emails/send-batch-emails
8 | def send(params = [], options: {})
9 | path = "emails/batch"
10 |
11 | Resend::Request.new(path, params, "post", options: options).perform
12 | end
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/lib/resend/broadcasts.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Resend
4 | # broadcasts api wrapper
5 | module Broadcasts
6 | class << self
7 | # https://resend.com/docs/api-reference/broadcasts/create-broadcast
8 | def create(params = {})
9 | path = "broadcasts"
10 | Resend::Request.new(path, params, "post").perform
11 | end
12 |
13 | # https://resend.com/docs/api-reference/broadcasts/update-broadcast
14 | def update(params = {})
15 | path = "broadcasts/#{params[:broadcast_id]}"
16 | Resend::Request.new(path, params, "patch").perform
17 | end
18 |
19 | # https://resend.com/docs/api-reference/broadcasts/send-broadcast
20 | def send(params = {})
21 | path = "broadcasts/#{params[:broadcast_id]}/send"
22 | Resend::Request.new(path, params, "post").perform
23 | end
24 |
25 | # https://resend.com/docs/api-reference/broadcasts/list-broadcasts
26 | def list
27 | path = "broadcasts"
28 | Resend::Request.new(path, {}, "get").perform
29 | end
30 |
31 | # https://resend.com/docs/api-reference/broadcasts/delete-broadcast
32 | def remove(broadcast_id = "")
33 | path = "broadcasts/#{broadcast_id}"
34 | Resend::Request.new(path, {}, "delete").perform
35 | end
36 |
37 | # https://resend.com/docs/api-reference/broadcasts/get-broadcast
38 | def get(broadcast_id = "")
39 | path = "broadcasts/#{broadcast_id}"
40 | Resend::Request.new(path, {}, "get").perform
41 | end
42 | end
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/lib/resend/client.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # backwards compatibility
4 | require "resend/emails"
5 |
6 | module Resend
7 | # Client class.
8 | class Client
9 | include Resend::Emails
10 |
11 | attr_reader :api_key
12 |
13 | def initialize(api_key)
14 | raise ArgumentError, "API Key is not a string" unless api_key.is_a?(String)
15 |
16 | @api_key = api_key
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/lib/resend/contacts.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Resend
4 | # Contacts api wrapper
5 | module Contacts
6 | class << self
7 | # https://resend.com/docs/api-reference/contacts/create-contact
8 | def create(params)
9 | path = "audiences/#{params[:audience_id]}/contacts"
10 | Resend::Request.new(path, params, "post").perform
11 | end
12 |
13 | #
14 | # Retrieves a contact from an audience
15 | #
16 | # @param audience_id [String] the audience id
17 | # @param id [String] either the contact id or contact's email
18 | #
19 | # https://resend.com/docs/api-reference/contacts/get-contact
20 | def get(audience_id, id)
21 | path = "audiences/#{audience_id}/contacts/#{id}"
22 | Resend::Request.new(path, {}, "get").perform
23 | end
24 |
25 | #
26 | # List contacts in an audience
27 | #
28 | # @param audience_id [String] the audience id
29 | # https://resend.com/docs/api-reference/contacts/list-contacts
30 | def list(audience_id)
31 | path = "audiences/#{audience_id}/contacts"
32 | Resend::Request.new(path, {}, "get").perform
33 | end
34 |
35 | #
36 | # Remove a contact from an audience
37 | #
38 | # @param audience_id [String] the audience id
39 | # @param contact_id [String] either the contact id or contact email
40 | #
41 | # see also: https://resend.com/docs/api-reference/contacts/delete-contact
42 | def remove(audience_id, contact_id)
43 | path = "audiences/#{audience_id}/contacts/#{contact_id}"
44 | Resend::Request.new(path, {}, "delete").perform
45 | end
46 |
47 | #
48 | # Update a contact
49 | #
50 | # @param params [Hash] the contact params
51 | # https://resend.com/docs/api-reference/contacts/update-contact
52 | def update(params)
53 | raise ArgumentError, "id or email is required" if params[:id].nil? && params[:email].nil?
54 |
55 | path = "audiences/#{params[:audience_id]}/contacts/#{params[:id] || params[:email]}"
56 | Resend::Request.new(path, params, "patch").perform
57 | end
58 | end
59 | end
60 | end
61 |
--------------------------------------------------------------------------------
/lib/resend/domains.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Resend
4 | # domains api wrapper
5 | module Domains
6 | class << self
7 | # https://resend.com/docs/api-reference/domains/create-domain
8 | def create(params)
9 | path = "domains"
10 | Resend::Request.new(path, params, "post").perform
11 | end
12 |
13 | # https://resend.com/docs/api-reference/domains/update-domain
14 | def update(params)
15 | path = "domains/#{params[:id]}"
16 | Resend::Request.new(path, params, "patch").perform
17 | end
18 |
19 | # https://resend.com/docs/api-reference/domains/get-domain
20 | def get(domain_id = "")
21 | path = "domains/#{domain_id}"
22 | Resend::Request.new(path, {}, "get").perform
23 | end
24 |
25 | # https://resend.com/docs/api-reference/api-keys/list-api-keys
26 | def list
27 | path = "domains"
28 | Resend::Request.new(path, {}, "get").perform
29 | end
30 |
31 | # https://resend.com/docs/api-reference/domains/delete-domain
32 | def remove(domain_id = "")
33 | path = "domains/#{domain_id}"
34 | Resend::Request.new(path, {}, "delete").perform
35 | end
36 |
37 | # https://resend.com/docs/api-reference/domains/verify-domain
38 | def verify(domain_id = "")
39 | path = "domains/#{domain_id}/verify"
40 | Resend::Request.new(path, {}, "post").perform
41 | end
42 | end
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/lib/resend/emails.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Resend
4 | # Module responsible for wrapping email sending API
5 | module Emails
6 | class << self
7 | # Sends or schedules an email.
8 | # see more: https://resend.com/docs/api-reference/emails/send-email
9 | def send(params, options: {})
10 | path = "emails"
11 | Resend::Request.new(path, params, "post", options: options).perform
12 | end
13 |
14 | # Retrieve a single email.
15 | # see more: https://resend.com/docs/api-reference/emails/retrieve-email
16 | def get(email_id = "")
17 | path = "emails/#{email_id}"
18 | Resend::Request.new(path, {}, "get").perform
19 | end
20 |
21 | # Update a scheduled email.
22 | # see more: https://resend.com/docs/api-reference/emails/update-email
23 | def update(params)
24 | path = "emails/#{params[:email_id]}"
25 | Resend::Request.new(path, params, "patch").perform
26 | end
27 |
28 | # Cancel a scheduled email.
29 | # see more: https://resend.com/docs/api-reference/emails/cancel-email
30 | def cancel(email_id = "")
31 | path = "emails/#{email_id}/cancel"
32 | Resend::Request.new(path, {}, "post").perform
33 | end
34 | end
35 |
36 | # This method is kept here for backwards compatibility
37 | # Use Resend::Emails.send instead.
38 | def send_email(params)
39 | warn "[DEPRECATION] `send_email` is deprecated. Please use `Resend::Emails.send` instead."
40 | Resend::Emails.send(params)
41 | end
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/lib/resend/errors.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Resend
4 | # Errors wrapper class
5 | # For more info: https://resend.com/docs/api-reference/error-codes
6 | class Error < StandardError
7 | # 4xx HTTP status code
8 | ClientError = Class.new(self)
9 |
10 | # 5xx HTTP status code
11 | ServerError = Class.new(self)
12 |
13 | # code 500
14 | InternalServerError = Class.new(ServerError)
15 |
16 | # code 422
17 | InvalidRequestError = Class.new(ServerError)
18 |
19 | # code 429
20 | RateLimitExceededError = Class.new(ServerError)
21 |
22 | # code 404
23 | NotFoundError = Class.new(ServerError)
24 |
25 | ERRORS = {
26 | 401 => Resend::Error::InvalidRequestError,
27 | 404 => Resend::Error::InvalidRequestError,
28 | 422 => Resend::Error::InvalidRequestError,
29 | 429 => Resend::Error::RateLimitExceededError,
30 | 400 => Resend::Error::InvalidRequestError,
31 | 500 => Resend::Error::InternalServerError
32 | }.freeze
33 |
34 | def initialize(msg, code = nil)
35 | super(msg)
36 | @code = code
37 | end
38 | end
39 | end
40 |
--------------------------------------------------------------------------------
/lib/resend/mailer.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "resend"
4 |
5 | module Resend
6 | # Mailer class used by railtie
7 | class Mailer
8 | attr_accessor :config, :settings
9 |
10 | # These are set as `headers` by the Rails API, but these will be filtered out
11 | # when constructing the Resend API payload, since they're are sent as post params.
12 | # https://resend.com/docs/api-reference/emails/send-email
13 | IGNORED_HEADERS = %w[
14 | cc bcc
15 | from reply-to to subject mime-version
16 | html text
17 | content-type tags scheduled_at
18 | headers options
19 | ].freeze
20 |
21 | SUPPORTED_OPTIONS = %w[idempotency_key].freeze
22 |
23 | def initialize(config)
24 | @config = config
25 | raise Resend::Error.new("Make sure your API Key is set", @config) unless Resend.api_key
26 |
27 | # avoids NilError exception
28 | @settings = { return_response: true }
29 | end
30 |
31 | #
32 | # Overwritten deliver! method
33 | #
34 | # @param Mail mail
35 | #
36 | # @return Object resend response
37 | #
38 | def deliver!(mail)
39 | params = build_resend_params(mail)
40 | options = get_options(mail) if mail[:options].present?
41 | resp = Resend::Emails.send(params, options: options || {})
42 | mail.message_id = resp[:id] if resp[:error].nil?
43 | resp
44 | end
45 |
46 | #
47 | # Builds the payload for sending
48 | #
49 | # @param Mail mail rails mail object
50 | #
51 | # @return Hash hash with all Resend params
52 | #
53 | def build_resend_params(mail)
54 | params = {
55 | from: get_from(mail),
56 | to: mail.to,
57 | subject: mail.subject
58 | }
59 | params.merge!(get_addons(mail))
60 | params.merge!(get_headers(mail))
61 | params.merge!(get_tags(mail))
62 | params[:attachments] = get_attachments(mail) if mail.attachments.present?
63 | params.merge!(get_contents(mail))
64 | params
65 | end
66 |
67 | #
68 | # Add custom headers fields.
69 | #
70 | # Both ways are supported:
71 | #
72 | # 1. Through the `#mail()` method ie:
73 | # mail(headers: { "X-Custom-Header" => "value" })
74 | #
75 | # 2. Through the Rails `#headers` method ie:
76 | # headers["X-Custom-Header"] = "value"
77 | #
78 | #
79 | # setting the header values through the `#mail` method will overwrite values set
80 | # through the `#headers` method using the same key.
81 | #
82 | # @param Mail mail Rails Mail object
83 | #
84 | # @return Hash hash with headers param
85 | #
86 | def get_headers(mail)
87 | params = {}
88 |
89 | if mail[:headers].present? || unignored_headers(mail).present?
90 | params[:headers] = {}
91 | params[:headers].merge!(headers_values(mail)) if unignored_headers(mail).present?
92 | params[:headers].merge!(mail_headers_values(mail)) if mail[:headers].present?
93 | end
94 |
95 | params
96 | end
97 |
98 | #
99 | # Adds additional options fields.
100 | # Currently supports only :idempotency_key
101 | #
102 | # @param Mail mail Rails Mail object
103 | # @return Hash hash with headers param
104 | #
105 | def get_options(mail)
106 | opts = {}
107 | if mail[:options].present?
108 | opts.merge!(mail[:options].unparsed_value)
109 | opts.delete_if { |k, _v| !SUPPORTED_OPTIONS.include?(k.to_s) }
110 | end
111 | opts
112 | end
113 |
114 | # Remove nils from header values
115 | def cleanup_headers(headers)
116 | headers.delete_if { |_k, v| v.nil? }
117 | end
118 |
119 | # Gets the values of the headers that are set through the `#mail` method
120 | #
121 | # @param Mail mail Rails Mail object
122 | # @return Hash hash with mail headers values
123 | def mail_headers_values(mail)
124 | params = {}
125 | mail[:headers].unparsed_value.each do |k, v|
126 | params[k.to_s] = v
127 | end
128 | cleanup_headers(params)
129 | params
130 | end
131 |
132 | # Gets the values of the headers that are set through the `#headers` method
133 | #
134 | # @param Mail mail Rails Mail object
135 | # @return Hash hash with headers values
136 | def headers_values(mail)
137 | params = {}
138 | unignored_headers(mail).each do |h|
139 | params[h.name.to_s] = h.unparsed_value
140 | end
141 | cleanup_headers(params)
142 | params
143 | end
144 |
145 | #
146 | # Add tags fields
147 | #
148 | # @param Mail mail Rails Mail object
149 | #
150 | # @return Hash hash with tags param
151 | #
152 | def get_tags(mail)
153 | params = {}
154 | params[:tags] = mail[:tags].unparsed_value if mail[:tags].present?
155 | params
156 | end
157 |
158 | #
159 | # Add cc, bcc, reply_to fields
160 | #
161 | # @param Mail mail Rails Mail Object
162 | #
163 | # @return Hash hash containing cc/bcc/reply_to attrs
164 | #
165 | def get_addons(mail)
166 | params = {}
167 | params[:cc] = mail.cc if mail.cc.present?
168 | params[:bcc] = mail.bcc if mail.bcc.present?
169 | params[:reply_to] = mail.reply_to if mail.reply_to.present?
170 | params
171 | end
172 |
173 | #
174 | # Gets the body of the email
175 | #
176 | # @param Mail mail Rails Mail Object
177 | #
178 | # @return Hash hash containing html/text or both attrs
179 | #
180 | def get_contents(mail)
181 | params = {}
182 | case mail.mime_type
183 | when "text/plain"
184 | params[:text] = mail.body.decoded
185 | when "text/html"
186 | params[:html] = mail.body.decoded
187 | when "multipart/alternative", "multipart/mixed", "multipart/related"
188 | params[:text] = mail.text_part.decoded if mail.text_part
189 | params[:html] = mail.html_part.decoded if mail.html_part
190 | end
191 | params
192 | end
193 |
194 | #
195 | # Properly gets the `from` attr
196 | #
197 | # @param Mail input object
198 | #
199 | # @return String `from` string
200 | #
201 | def get_from(input)
202 | return input.from.first if input[:from].nil?
203 |
204 | from = input[:from].formatted
205 | return from.first if from.is_a? Array
206 |
207 | from.to_s
208 | end
209 |
210 | #
211 | # Handle attachments when present
212 | #
213 | # @return Array attachments array
214 | #
215 | def get_attachments(mail)
216 | attachments = []
217 | mail.attachments.each do |part|
218 | attachment = {
219 | filename: part.filename,
220 | content: part.body.decoded.bytes
221 | }
222 | attachments.append(attachment)
223 | end
224 | attachments
225 | end
226 |
227 | #
228 | # Get all headers that are not ignored
229 | #
230 | # @param Mail mail
231 | #
232 | # @return Array headers
233 | #
234 | def unignored_headers(mail)
235 | @unignored_headers ||= mail.header_fields.reject { |h| IGNORED_HEADERS.include?(h.name.downcase) }
236 | end
237 | end
238 | end
239 |
--------------------------------------------------------------------------------
/lib/resend/railtie.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "resend"
4 | require "resend/mailer"
5 |
6 | module Resend
7 | # Main railtime class
8 | class Railtie < ::Rails::Railtie
9 | ActiveSupport.on_load(:action_mailer) do
10 | add_delivery_method :resend, Resend::Mailer
11 | ActiveSupport.run_load_hooks(:resend_mailer, Resend::Mailer)
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/lib/resend/request.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Resend
4 | # This class is responsible for making the appropriate HTTP calls
5 | # and raising the specific errors based on the response.
6 | class Request
7 | BASE_URL = ENV["RESEND_BASE_URL"] || "https://api.resend.com/"
8 |
9 | attr_accessor :body, :verb, :options
10 |
11 | def initialize(path = "", body = {}, verb = "POST", options: {})
12 | raise if (api_key = Resend.api_key).nil?
13 |
14 | api_key = api_key.call if api_key.is_a?(Proc)
15 |
16 | @path = path
17 | @body = body
18 | @verb = verb
19 | @options = options
20 | @headers = {
21 | "Content-Type" => "application/json",
22 | "Accept" => "application/json",
23 | "User-Agent" => "resend-ruby:#{Resend::VERSION}",
24 | "Authorization" => "Bearer #{api_key}"
25 | }
26 |
27 | set_idempotency_key
28 | end
29 |
30 | # Performs the HTTP call
31 | def perform
32 | options = {
33 | headers: @headers
34 | }
35 | options[:body] = @body.to_json unless @body.empty?
36 |
37 | resp = HTTParty.send(@verb.to_sym, "#{BASE_URL}#{@path}", options)
38 |
39 | check_json!(resp)
40 |
41 | resp.transform_keys!(&:to_sym) unless resp.body.empty?
42 | handle_error!(resp) if resp[:statusCode] && (resp[:statusCode] != 200 || resp[:statusCode] != 201)
43 | resp
44 | end
45 |
46 | def handle_error!(resp)
47 | code = resp[:statusCode]
48 | body = resp[:message]
49 |
50 | # get error from the known list of errors
51 | error = Resend::Error::ERRORS[code]
52 | raise(error.new(body, code)) if error
53 |
54 | # Raise generic Resend error when the error code is not part of the known errors
55 | raise Resend::Error.new(body, code)
56 | end
57 |
58 | private
59 |
60 | def set_idempotency_key
61 | # Only set idempotency key if the verb is POST for now.
62 | #
63 | # Does not set it if the idempotency_key is nil or empty
64 | if @verb.downcase == "post" && (!@options[:idempotency_key].nil? && !@options[:idempotency_key].empty?)
65 | @headers["Idempotency-Key"] = @options[:idempotency_key]
66 | end
67 | end
68 |
69 | def check_json!(resp)
70 | if resp.body.is_a?(Hash)
71 | JSON.parse(resp.body.to_json)
72 | else
73 | JSON.parse(resp.body)
74 | end
75 | rescue JSON::ParserError, TypeError
76 | raise Resend::Error::InternalServerError.new("Resend API returned an unexpected response", nil)
77 | end
78 | end
79 | end
80 |
--------------------------------------------------------------------------------
/lib/resend/version.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module Resend
4 | VERSION = "0.22.0"
5 | end
6 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "config:base"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/resend.gemspec:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative "lib/resend/version"
4 |
5 | Gem::Specification.new do |spec|
6 | spec.name = "resend"
7 | spec.version = Resend::VERSION
8 | spec.summary = "The Ruby and Rails SDK for resend.com"
9 | spec.homepage = "https://github.com/resend/resend-ruby"
10 | spec.license = "MIT"
11 |
12 | spec.author = "Derich Pacheco"
13 | spec.email = "carlosderich@gmail.com"
14 |
15 | spec.files = Dir["*.{md,txt}", "{lib}/**/*"]
16 | spec.require_path = "lib"
17 | spec.required_ruby_version = ">= 2.6"
18 | spec.add_dependency "httparty", ">= 0.21.0"
19 | spec.add_development_dependency "rails"
20 | end
21 |
--------------------------------------------------------------------------------
/resources/invoice.pdf:
--------------------------------------------------------------------------------
1 | %PDF-1.7
2 |
3 | 1 0 obj
4 | << /Length 2 0 R >>
5 | stream
6 | 0.574574 0 0.040483 -0.011009 0.511009 0.604048 d1
7 |
8 | endstream
9 | endobj
10 |
11 | 2 0 obj
12 | 51
13 | endobj
14 |
15 | 3 0 obj
16 | << /Length 4 0 R >>
17 | stream
18 | 0.556818 0 0.034091 -0.204545 0.522727 0.784091 d1
19 |
20 | endstream
21 | endobj
22 |
23 | 4 0 obj
24 | 51
25 | endobj
26 |
27 | 5 0 obj
28 | << /Length 6 0 R >>
29 | stream
30 | 0.248224 0 0.000000 0.000000 0.248224 1.000000 d1
31 |
32 | endstream
33 | endobj
34 |
35 | 6 0 obj
36 | 50
37 | endobj
38 |
39 | 7 0 obj
40 | << /Length 8 0 R >>
41 | stream
42 | 0.575994 0 0.022727 -0.204545 0.553267 0.772727 d1
43 |
44 | endstream
45 | endobj
46 |
47 | 8 0 obj
48 | 51
49 | endobj
50 |
51 | 9 0 obj
52 | << /Length 10 0 R >>
53 | stream
54 | 0.643466 0 0.071733 0.000000 0.600852 0.799006 d1
55 |
56 | endstream
57 | endobj
58 |
59 | 10 0 obj
60 | 50
61 | endobj
62 |
63 | 11 0 obj
64 | << /Length 12 0 R >>
65 | stream
66 | 0.596591 0 0.051136 -0.011364 0.545455 0.615057 d1
67 |
68 | endstream
69 | endobj
70 |
71 | 12 0 obj
72 | 51
73 | endobj
74 |
75 | 13 0 obj
76 | << /Length 14 0 R >>
77 | stream
78 | 0.609730 0 0.066051 0.000000 0.545099 0.618608 d1
79 |
80 | endstream
81 | endobj
82 |
83 | 14 0 obj
84 | 50
85 | endobj
86 |
87 | 15 0 obj
88 | << /Length 16 0 R >>
89 | stream
90 | 0.281250 0 0.000000 0.000000 0.281250 1.000000 d1
91 |
92 | endstream
93 | endobj
94 |
95 | 16 0 obj
96 | 50
97 | endobj
98 |
99 | 17 0 obj
100 | << /Length 18 0 R >>
101 | stream
102 | 0.650568 0 0.071733 0.000000 0.627486 0.799006 d1
103 |
104 | endstream
105 | endobj
106 |
107 | 18 0 obj
108 | 50
109 | endobj
110 |
111 | 19 0 obj
112 | << /Length 20 0 R >>
113 | stream
114 | 0.563920 0 0.051136 -0.012784 0.487216 0.616477 d1
115 |
116 | endstream
117 | endobj
118 |
119 | 20 0 obj
120 | 51
121 | endobj
122 |
123 | 21 0 obj
124 | << /Length 22 0 R >>
125 | stream
126 | 0.607955 0 0.042614 -0.010653 0.565341 0.605824 d1
127 |
128 | endstream
129 | endobj
130 |
131 | 22 0 obj
132 | 51
133 | endobj
134 |
135 | 23 0 obj
136 | << /Length 24 0 R >>
137 | stream
138 | 0.590909 0 0.076705 0.000000 0.514205 0.803977 d1
139 |
140 | endstream
141 | endobj
142 |
143 | 24 0 obj
144 | 50
145 | endobj
146 |
147 | 25 0 obj
148 | << /Length 26 0 R >>
149 | stream
150 | 0.608310 0 0.066051 -0.007102 0.542259 0.618608 d1
151 |
152 | endstream
153 | endobj
154 |
155 | 26 0 obj
156 | 51
157 | endobj
158 |
159 | 27 0 obj
160 | << /Length 28 0 R >>
161 | stream
162 | 0.869318 0 0.076705 0.000000 0.792614 0.629261 d1
163 |
164 | endstream
165 | endobj
166 |
167 | 28 0 obj
168 | 50
169 | endobj
170 |
171 | 29 0 obj
172 | << /Length 30 0 R >>
173 | stream
174 | 0.577415 0 0.042614 -0.010653 0.537642 0.605824 d1
175 |
176 | endstream
177 | endobj
178 |
179 | 30 0 obj
180 | 51
181 | endobj
182 |
183 | 31 0 obj
184 | << /Length 32 0 R >>
185 | stream
186 | 0.642045 0 0.048295 0.000000 0.593750 0.775568 d1
187 |
188 | endstream
189 | endobj
190 |
191 | 32 0 obj
192 | 50
193 | endobj
194 |
195 | 33 0 obj
196 | << /Length 34 0 R >>
197 | stream
198 | 0.379972 0 0.024858 -0.008878 0.348366 0.709872 d1
199 |
200 | endstream
201 | endobj
202 |
203 | 34 0 obj
204 | 51
205 | endobj
206 |
207 | 35 0 obj
208 | << /Length 36 0 R >>
209 | stream
210 | 0.897727 0 0.066051 0.000000 0.833097 0.618608 d1
211 |
212 | endstream
213 | endobj
214 |
215 | 36 0 obj
216 | 50
217 | endobj
218 |
219 | 37 0 obj
220 | << /Length 38 0 R >>
221 | stream
222 | 0.396662 0 0.066051 0.000000 0.377131 0.619318 d1
223 |
224 | endstream
225 | endobj
226 |
227 | 38 0 obj
228 | 50
229 | endobj
230 |
231 | 39 0 obj
232 | << /Length 40 0 R >>
233 | stream
234 | 0.377131 0 0.019886 0.000000 0.368608 0.781250 d1
235 |
236 | endstream
237 | endobj
238 |
239 | 40 0 obj
240 | 50
241 | endobj
242 |
243 | 41 0 obj
244 | << /Length 42 0 R >>
245 | stream
246 | 0.260653 0 0.056108 0.000000 0.205256 0.818537 d1
247 |
248 | endstream
249 | endobj
250 |
251 | 42 0 obj
252 | 50
253 | endobj
254 |
255 | 43 0 obj
256 | << /Length 44 0 R >>
257 | stream
258 | 0.363636 0 0.031250 -0.007102 0.323864 0.714489 d1
259 |
260 | endstream
261 | endobj
262 |
263 | 44 0 obj
264 | 51
265 | endobj
266 |
267 | 45 0 obj
268 | << /Length 46 0 R >>
269 | stream
270 | 0.624645 0 0.066051 -0.204545 0.580966 0.823153 d1
271 |
272 | endstream
273 | endobj
274 |
275 | 46 0 obj
276 | 51
277 | endobj
278 |
279 | 47 0 obj
280 | << /Length 48 0 R >>
281 | stream
282 | 0.360795 0 0.025568 0.000000 0.346591 0.786932 d1
283 |
284 | endstream
285 | endobj
286 |
287 | 48 0 obj
288 | 50
289 | endobj
290 |
291 | 49 0 obj
292 | << /Length 50 0 R >>
293 | stream
294 | 0.592685 0 0.042614 -0.010653 0.550071 0.605824 d1
295 |
296 | endstream
297 | endobj
298 |
299 | 50 0 obj
300 | 51
301 | endobj
302 |
303 | 51 0 obj
304 | << /Length 52 0 R >>
305 | stream
306 | 0.275568 0 0.073864 -0.005682 0.201705 0.201705 d1
307 |
308 | endstream
309 | endobj
310 |
311 | 52 0 obj
312 | 51
313 | endobj
314 |
315 | 53 0 obj
316 | << /Length 54 0 R >>
317 | stream
318 | 0.582386 0 0.051136 -0.011364 0.531250 0.615057 d1
319 |
320 | endstream
321 | endobj
322 |
323 | 54 0 obj
324 | 51
325 | endobj
326 |
327 | 55 0 obj
328 | << /Length 56 0 R >>
329 | stream
330 | 0.704545 0 0.022017 0.000000 0.682528 0.749290 d1
331 |
332 | endstream
333 | endobj
334 |
335 | 56 0 obj
336 | 50
337 | endobj
338 |
339 | 57 0 obj
340 | << /Length 58 0 R >>
341 | stream
342 | 0.609375 0 0.076705 -0.204545 0.558239 0.833807 d1
343 |
344 | endstream
345 | endobj
346 |
347 | 58 0 obj
348 | 51
349 | endobj
350 |
351 | 59 0 obj
352 | << /Length 60 0 R >>
353 | stream
354 | 0.522727 0 0.052557 -0.011364 0.473011 0.616477 d1
355 |
356 | endstream
357 | endobj
358 |
359 | 60 0 obj
360 | 51
361 | endobj
362 |
363 | 61 0 obj
364 | << /Length 62 0 R >>
365 | stream
366 | 0.585227 0 0.076705 0.000000 0.508523 0.629261 d1
367 |
368 | endstream
369 | endobj
370 |
371 | 62 0 obj
372 | 50
373 | endobj
374 |
375 | 63 0 obj
376 | << /Length 64 0 R >>
377 | stream
378 | 0.544034 0 0.076705 0.000000 0.529830 0.803977 d1
379 |
380 | endstream
381 | endobj
382 |
383 | 64 0 obj
384 | 50
385 | endobj
386 |
387 | 65 0 obj
388 | << /Length 66 0 R >>
389 | stream
390 | 0.372159 0 0.076705 0.000000 0.349432 0.630682 d1
391 |
392 | endstream
393 | endobj
394 |
395 | 66 0 obj
396 | 50
397 | endobj
398 |
399 | 67 0 obj
400 | [ 0.609730 0.897727 0.575994 0.574574 0.643466 0.608310 0.704545 0.607955 0.379972 0.248224 0.396662 0.377131 0.260653 0.624645 0.592685 0.577415 0.650568 0.275568 0.556818 0.582386 0.869318 0.363636 0.360795 0.522727 0.281250 0.585227 0.544034 0.563920 0.590909 0.372159 0.596591 0.609375 0.642045 ]
401 | endobj
402 |
403 | 68 0 obj
404 | << /Length 69 0 R >>
405 | stream
406 | /CIDInit /ProcSet findresource begin
407 | 12 dict begin
408 | begincmap
409 | /CIDSystemInfo
410 | << /Registry (FigmaPDF)
411 | /Ordering (FigmaPDF)
412 | /Supplement 0
413 | >> def
414 | /CMapName /A-B-C def
415 | /CMapType 2 def
416 | 1 begincodespacerange
417 | <00>
418 | endcodespacerange
419 | 1 beginbfchar
420 | <00> <006E>
421 | endbfchar
422 | 1 beginbfchar
423 | <01> <006D>
424 | endbfchar
425 | 1 beginbfchar
426 | <02> <0079>
427 | endbfchar
428 | 1 beginbfchar
429 | <03> <0061>
430 | endbfchar
431 | 1 beginbfchar
432 | <04> <0050>
433 | endbfchar
434 | 1 beginbfchar
435 | <05> <0075>
436 | endbfchar
437 | 1 beginbfchar
438 | <06> <0059>
439 | endbfchar
440 | 1 beginbfchar
441 | <07> <006F>
442 | endbfchar
443 | 1 beginbfchar
444 | <08> <0074>
445 | endbfchar
446 | 1 beginbfchar
447 | <09> <0020>
448 | endbfchar
449 | 1 beginbfchar
450 | <0A> <0072>
451 | endbfchar
452 | 1 beginbfchar
453 | <0B> <0066>
454 | endbfchar
455 | 1 beginbfchar
456 | <0C> <0069>
457 | endbfchar
458 | 1 beginbfchar
459 | <0D> <0070>
460 | endbfchar
461 | 1 beginbfchar
462 | <0E> <0065>
463 | endbfchar
464 | 1 beginbfchar
465 | <0F> <0063>
466 | endbfchar
467 | 1 beginbfchar
468 | <10> <0052>
469 | endbfchar
470 | 1 beginbfchar
471 | <11> <002E>
472 | endbfchar
473 | 1 beginbfchar
474 | <12> <0079>
475 | endbfchar
476 | 1 beginbfchar
477 | <13> <0065>
478 | endbfchar
479 | 1 beginbfchar
480 | <14> <006D>
481 | endbfchar
482 | 1 beginbfchar
483 | <15> <0074>
484 | endbfchar
485 | 1 beginbfchar
486 | <16> <0066>
487 | endbfchar
488 | 1 beginbfchar
489 | <17> <0073>
490 | endbfchar
491 | 1 beginbfchar
492 | <18> <0020>
493 | endbfchar
494 | 1 beginbfchar
495 | <19> <006E>
496 | endbfchar
497 | 1 beginbfchar
498 | <1A> <006B>
499 | endbfchar
500 | 1 beginbfchar
501 | <1B> <0061>
502 | endbfchar
503 | 1 beginbfchar
504 | <1C> <0068>
505 | endbfchar
506 | 1 beginbfchar
507 | <1D> <0072>
508 | endbfchar
509 | 1 beginbfchar
510 | <1E> <006F>
511 | endbfchar
512 | 1 beginbfchar
513 | <1F> <0070>
514 | endbfchar
515 | 1 beginbfchar
516 | <20> <0054>
517 | endbfchar
518 | endcmap
519 | CMapName currentdict /CMap defineresource pop
520 | end
521 | end
522 | endstream
523 | endobj
524 |
525 | 69 0 obj
526 | 1484
527 | endobj
528 |
529 | 70 0 obj
530 | << /Subtype /Type3
531 | /CharProcs << /C18 3 0 R
532 | /C3 1 0 R
533 | /C2 7 0 R
534 | /C30 11 0 R
535 | /C4 9 0 R
536 | /C11 39 0 R
537 | /C9 5 0 R
538 | /C0 13 0 R
539 | /C24 15 0 R
540 | /C27 19 0 R
541 | /C16 17 0 R
542 | /C7 21 0 R
543 | /C28 23 0 R
544 | /C12 41 0 R
545 | /C21 43 0 R
546 | /C5 25 0 R
547 | /C20 27 0 R
548 | /C15 29 0 R
549 | /C32 31 0 R
550 | /C8 33 0 R
551 | /C1 35 0 R
552 | /C10 37 0 R
553 | /C13 45 0 R
554 | /C22 47 0 R
555 | /C14 49 0 R
556 | /C17 51 0 R
557 | /C19 53 0 R
558 | /C6 55 0 R
559 | /C31 57 0 R
560 | /C23 59 0 R
561 | /C25 61 0 R
562 | /C26 63 0 R
563 | /C29 65 0 R
564 | >>
565 | /Encoding << /Type /Encoding
566 | /Differences [ 0 /C0 /C1 /C2 /C3 /C4 /C5 /C6 /C7 /C8 /C9 /C10 /C11 /C12 /C13 /C14 /C15 /C16 /C17 /C18 /C19 /C20 /C21 /C22 /C23 /C24 /C25 /C26 /C27 /C28 /C29 /C30 /C31 /C32 ]
567 | >>
568 | /Widths 67 0 R
569 | /FontBBox [ 0.000000 0.000000 0.000000 0.000000 ]
570 | /FontMatrix [ 1.000000 0.000000 0.000000 1.000000 0.000000 0.000000 ]
571 | /Type /Font
572 | /ToUnicode 68 0 R
573 | /FirstChar 0
574 | /LastChar 32
575 | /Resources << >>
576 | >>
577 | endobj
578 |
579 | 71 0 obj
580 | << /Length 72 0 R
581 | /Range [ 0.000000 1.000000 ]
582 | /Domain [ 0.000000 1.000000 ]
583 | /FunctionType 4
584 | >>
585 | stream
586 | { 1.000000 exch dup 0.000000 gt { exch pop dup 0.000000 sub -1.000000 mul 1.000000 add exch } if dup 1.000000 gt { exch pop 0.000000 exch } if pop }
587 | endstream
588 | endobj
589 |
590 | 72 0 obj
591 | 149
592 | endobj
593 |
594 | 73 0 obj
595 | << /BBox [ 0.000000 0.000000 500.000000 700.000000 ]
596 | /Resources << /Pattern << /P1 << /Matrix [ 35.064983 -28.864431 28.864431 35.064983 43.777412 595.760620 ]
597 | /Shading << /Coords [ 0.000000 0.000000 1.000000 0.000000 ]
598 | /ColorSpace /DeviceGray
599 | /Function 71 0 R
600 | /Domain [ 0.000000 1.000000 ]
601 | /ShadingType 2
602 | /Extend [ true true ]
603 | >>
604 | /PatternType 2
605 | /Type /Pattern
606 | >> >> >>
607 | /Subtype /Form
608 | /Length 74 0 R
609 | /Group << /Type /Group
610 | /S /Transparency
611 | /CS /DeviceGray
612 | >>
613 | /Type /XObject
614 | >>
615 | stream
616 | /DeviceGray CS
617 | /DeviceGray cs
618 | 1.000000 0.000000 -0.000000 1.000000 71.476868 612.707703 cm
619 | 0.000000 -0.000017 m
620 | 0.000000 19.076904 l
621 | 7.247755 19.076904 l
622 | 8.732555 19.076904 9.978277 18.822321 10.984923 18.313089 c
623 | 11.997847 17.803858 12.762276 17.089705 13.278153 16.170628 c
624 | 13.800368 15.257766 14.061415 14.192782 14.061415 12.975613 c
625 | 14.061415 11.752291 13.797171 10.690382 13.268740 9.789951 c
626 | 12.746524 8.895737 11.975815 8.203305 10.956615 7.712721 c
627 | 9.937353 7.228352 8.685353 6.986135 7.200614 6.986135 c
628 | 2.038462 6.986135 l
629 | 2.038462 9.855121 l
630 | 6.728738 9.855121 l
631 | 7.596922 9.855121 8.307877 9.973151 8.861539 10.209089 c
632 | 9.415200 10.451304 9.824122 10.802135 10.088368 11.261703 c
633 | 10.358891 11.727427 10.494154 12.298752 10.494154 12.975613 c
634 | 10.494154 13.652536 10.358891 14.230074 10.088368 14.708227 c
635 | 9.817845 15.192596 9.405723 15.558997 8.852122 15.807366 c
636 | 8.298461 16.061951 7.584368 16.189274 6.709845 16.189274 c
637 | 3.501231 16.189274 l
638 | 3.501231 -0.000017 l
639 | 0.000000 -0.000017 l
640 | h
641 | 9.984555 8.644228 m
642 | 14.769230 -0.000017 l
643 | 10.862215 -0.000017 l
644 | 6.162524 8.644228 l
645 | 9.984555 8.644228 l
646 | h
647 | /Pattern cs
648 | /P1 scn
649 | f
650 | n
651 |
652 | endstream
653 | endobj
654 |
655 | 74 0 obj
656 | 1128
657 | endobj
658 |
659 | 75 0 obj
660 | << /Length 76 0 R
661 | /Range [ 0.000000 1.000000 ]
662 | /Domain [ 0.000000 1.000000 ]
663 | /FunctionType 4
664 | >>
665 | stream
666 | { 0.600000 exch dup 0.000000 gt { exch pop dup 0.000000 sub -0.400000 mul 0.600000 add exch } if dup 1.000000 gt { exch pop 0.200000 exch } if pop }
667 | endstream
668 | endobj
669 |
670 | 76 0 obj
671 | 149
672 | endobj
673 |
674 | 77 0 obj
675 | << /BBox [ 0.000000 0.000000 500.000000 700.000000 ]
676 | /Resources << /Pattern << /P1 << /Matrix [ 0.000000 -44.307686 44.307686 0.000000 9.692314 644.153870 ]
677 | /Shading << /Coords [ 0.000000 0.000000 1.000000 0.000000 ]
678 | /ColorSpace /DeviceGray
679 | /Function 75 0 R
680 | /Domain [ 0.000000 1.000000 ]
681 | /ShadingType 2
682 | /Extend [ true true ]
683 | >>
684 | /PatternType 2
685 | /Type /Pattern
686 | >> >> >>
687 | /Subtype /Form
688 | /Length 78 0 R
689 | /Group << /Type /Group
690 | /S /Transparency
691 | /CS /DeviceGray
692 | >>
693 | /Type /XObject
694 | >>
695 | stream
696 | /DeviceGray CS
697 | /DeviceGray cs
698 | 1.000000 0.000000 -0.000000 1.000000 54.000000 598.000000 cm
699 | 30.481234 48.000000 m
700 | 33.169601 48.000015 35.300064 48.000027 37.017292 47.859722 c
701 | 38.773293 47.716248 40.259140 47.416904 41.615879 46.725613 c
702 | 43.815941 45.604633 45.604618 43.815937 46.725601 41.615875 c
703 | 47.416924 40.259136 47.716251 38.773293 47.859695 37.017292 c
704 | 48.000004 35.300060 48.000000 33.169601 48.000000 30.481293 c
705 | 48.000000 17.518705 l
706 | 48.000000 14.830399 48.000004 12.699936 47.859695 10.982708 c
707 | 47.716251 9.226707 47.416924 7.740860 46.725601 6.384121 c
708 | 45.604618 4.184059 43.815941 2.395382 41.615879 1.274399 c
709 | 40.259140 0.583076 38.773293 0.283749 37.017292 0.140305 c
710 | 35.300064 -0.000004 33.169601 0.000000 30.481295 0.000000 c
711 | 17.518707 0.000000 l
712 | 14.830400 0.000000 12.699940 -0.000004 10.982709 0.140305 c
713 | 9.226708 0.283749 7.740862 0.583076 6.384123 1.274399 c
714 | 4.184062 2.395382 2.395366 4.184059 1.274388 6.384121 c
715 | 0.583094 7.740860 0.283751 9.226707 0.140278 10.982708 c
716 | -0.000025 12.699936 -0.000014 14.830399 0.000000 17.518766 c
717 | 0.000000 30.481230 l
718 | -0.000014 33.169601 -0.000025 35.300060 0.140278 37.017292 c
719 | 0.283751 38.773293 0.583094 40.259136 1.274388 41.615875 c
720 | 2.395366 43.815937 4.184062 45.604633 6.384123 46.725613 c
721 | 7.740862 47.416908 9.226708 47.716248 10.982709 47.859722 c
722 | 12.699940 48.000027 14.830400 48.000015 17.518770 48.000000 c
723 | 30.481234 48.000000 l
724 | h
725 | 2.919323 40.777721 m
726 | 1.846154 38.671570 1.846154 35.914337 1.846154 30.400000 c
727 | 1.846154 17.599998 l
728 | 1.846154 12.085659 1.846154 9.328430 2.919323 7.222275 c
729 | 3.863305 5.369598 5.369576 3.863319 7.222277 2.919319 c
730 | 9.328431 1.846149 12.085662 1.846153 17.600000 1.846153 c
731 | 30.400002 1.846153 l
732 | 35.914341 1.846153 38.671570 1.846149 40.777725 2.919319 c
733 | 42.630402 3.863319 44.136681 5.369598 45.080681 7.222275 c
734 | 46.153851 9.328430 46.153847 12.085659 46.153847 17.599998 c
735 | 46.153847 30.400000 l
736 | 46.153847 35.914337 46.153851 38.671570 45.080681 40.777721 c
737 | 44.136681 42.630424 42.630402 44.136696 40.777725 45.080677 c
738 | 38.671570 46.153847 35.914341 46.153847 30.400002 46.153847 c
739 | 17.600000 46.153847 l
740 | 12.085662 46.153847 9.328431 46.153847 7.222277 45.080677 c
741 | 5.369576 44.136696 3.863305 42.630424 2.919323 40.777721 c
742 | h
743 | /Pattern cs
744 | /P1 scn
745 | f*
746 | n
747 |
748 | endstream
749 | endobj
750 |
751 | 78 0 obj
752 | 2249
753 | endobj
754 |
755 | 79 0 obj
756 | << /Length 80 0 R
757 | /Range [ 0.000000 1.000000 0.000000 1.000000 0.000000 1.000000 ]
758 | /Domain [ 0.000000 1.000000 ]
759 | /FunctionType 4
760 | >>
761 | stream
762 | { 1.000000 exch 1.000000 exch 1.000000 exch dup 0.000000 gt { exch pop exch pop exch pop dup 0.000000 sub 0.000000 mul 1.000000 add exch dup 0.000000 sub 0.000000 mul 1.000000 add exch dup 0.000000 sub 0.000000 mul 1.000000 add exch } if dup 1.000000 gt { exch pop exch pop exch pop 1.000000 exch 1.000000 exch 1.000000 exch } if pop }
763 | endstream
764 | endobj
765 |
766 | 80 0 obj
767 | 336
768 | endobj
769 |
770 | 81 0 obj
771 | << /Length 82 0 R
772 | /Range [ 0.000000 1.000000 0.000000 1.000000 0.000000 1.000000 ]
773 | /Domain [ 0.000000 1.000000 ]
774 | /FunctionType 4
775 | >>
776 | stream
777 | { 1.000000 exch 1.000000 exch 1.000000 exch dup 0.000000 gt { exch pop exch pop exch pop dup 0.000000 sub 0.000000 mul 1.000000 add exch dup 0.000000 sub 0.000000 mul 1.000000 add exch dup 0.000000 sub 0.000000 mul 1.000000 add exch } if dup 1.000000 gt { exch pop exch pop exch pop 1.000000 exch 1.000000 exch 1.000000 exch } if pop }
778 | endstream
779 | endobj
780 |
781 | 82 0 obj
782 | 336
783 | endobj
784 |
785 | 83 0 obj
786 | << /Length 84 0 R
787 | /Range [ 0.000000 1.000000 0.000000 1.000000 0.000000 1.000000 ]
788 | /Domain [ 0.000000 1.000000 ]
789 | /FunctionType 4
790 | >>
791 | stream
792 | { 0.298039 exch 0.298039 exch 0.341176 exch dup 0.000000 gt { exch pop exch pop exch pop dup 0.000000 sub -0.626471 mul 0.298039 add exch dup 0.000000 sub -0.626471 mul 0.298039 add exch dup 0.000000 sub -0.679412 mul 0.341176 add exch } if dup 0.444444 gt { exch pop exch pop exch pop dup 0.444444 sub -0.035294 mul 0.019608 add exch dup 0.444444 sub -0.035294 mul 0.019608 add exch dup 0.444444 sub -0.070588 mul 0.039216 add exch } if dup 1.000000 gt { exch pop exch pop exch pop 0.000000 exch 0.000000 exch 0.000000 exch } if pop }
793 | endstream
794 | endobj
795 |
796 | 84 0 obj
797 | 536
798 | endobj
799 |
800 | 85 0 obj
801 | << /Length 86 0 R
802 | /Range [ 0.000000 1.000000 0.000000 1.000000 0.000000 1.000000 ]
803 | /Domain [ 0.000000 1.000000 ]
804 | /FunctionType 4
805 | >>
806 | stream
807 | { 0.298039 exch 0.298039 exch 0.341176 exch dup 0.000000 gt { exch pop exch pop exch pop dup 0.000000 sub -0.626471 mul 0.298039 add exch dup 0.000000 sub -0.626471 mul 0.298039 add exch dup 0.000000 sub -0.679412 mul 0.341176 add exch } if dup 0.444444 gt { exch pop exch pop exch pop dup 0.444444 sub -0.035294 mul 0.019608 add exch dup 0.444444 sub -0.035294 mul 0.019608 add exch dup 0.444444 sub -0.070588 mul 0.039216 add exch } if dup 1.000000 gt { exch pop exch pop exch pop 0.000000 exch 0.000000 exch 0.000000 exch } if pop }
808 | endstream
809 | endobj
810 |
811 | 86 0 obj
812 | 536
813 | endobj
814 |
815 | 87 0 obj
816 | << /Font << /F1 70 0 R >>
817 | /ExtGState << /E2 << /SMask << /Type /Mask
818 | /G 73 0 R
819 | /S /Luminosity
820 | >>
821 | /Type /ExtGState
822 | >>
823 | /E1 << /SMask << /Type /Mask
824 | /G 77 0 R
825 | /S /Luminosity
826 | >>
827 | /Type /ExtGState
828 | >>
829 | >>
830 | /Pattern << /P4 << /Matrix [ 35.064983 -28.864431 28.864431 35.064983 43.777412 595.760620 ]
831 | /Shading << /Coords [ 0.000000 0.000000 1.000000 0.000000 ]
832 | /ColorSpace /DeviceRGB
833 | /Function 79 0 R
834 | /Domain [ 0.000000 1.000000 ]
835 | /ShadingType 2
836 | /Extend [ true true ]
837 | >>
838 | /PatternType 2
839 | /Type /Pattern
840 | >>
841 | /P3 << /Matrix [ 0.000000 -44.307686 44.307686 0.000000 9.692314 644.153870 ]
842 | /Shading << /Coords [ 0.000000 0.000000 1.000000 0.000000 ]
843 | /ColorSpace /DeviceRGB
844 | /Function 81 0 R
845 | /Domain [ 0.000000 1.000000 ]
846 | /ShadingType 2
847 | /Extend [ true true ]
848 | >>
849 | /PatternType 2
850 | /Type /Pattern
851 | >>
852 | /P2 << /Matrix [ 0.000000 -58.461540 58.461540 0.000000 -2.615410 658.307739 ]
853 | /Shading << /Coords [ 0.000000 0.000000 1.000000 0.000000 ]
854 | /ColorSpace /DeviceRGB
855 | /Function 83 0 R
856 | /Domain [ 0.000000 1.000000 ]
857 | /ShadingType 2
858 | /Extend [ true true ]
859 | >>
860 | /PatternType 2
861 | /Type /Pattern
862 | >>
863 | /P1 << /Matrix [ 0.000000 -58.461540 58.461540 0.000000 -2.615410 658.307739 ]
864 | /Shading << /Coords [ 0.000000 0.000000 1.000000 0.000000 ]
865 | /ColorSpace /DeviceRGB
866 | /Function 85 0 R
867 | /Domain [ 0.000000 1.000000 ]
868 | /ShadingType 2
869 | /Extend [ true true ]
870 | >>
871 | /PatternType 2
872 | /Type /Pattern
873 | >>
874 | >>
875 | >>
876 | endobj
877 |
878 | 88 0 obj
879 | << /Length 89 0 R >>
880 | stream
881 | /DeviceRGB CS
882 | /DeviceRGB cs
883 | q
884 | 1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
885 | 1.000000 1.000000 1.000000 scn
886 | 0.000000 700.000000 m
887 | 500.000000 700.000000 l
888 | 500.000000 0.000000 l
889 | 0.000000 0.000000 l
890 | 0.000000 700.000000 l
891 | h
892 | f
893 | n
894 | Q
895 | q
896 | 1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
897 | 0.000000 0.000000 0.000000 scn
898 | 0.000000 700.000000 m
899 | 500.000000 700.000000 l
900 | 500.000000 0.000000 l
901 | 0.000000 0.000000 l
902 | 0.000000 700.000000 l
903 | h
904 | f
905 | n
906 | Q
907 | q
908 | 1.000000 0.000000 -0.000000 1.000000 55.846130 599.846191 cm
909 | /Pattern cs
910 | /P1 scn
911 | 0.000000 28.553831 m
912 | 0.000000 34.068169 -0.000000 36.825401 1.073169 38.931557 c
913 | 2.017151 40.784256 3.523422 42.290527 5.376123 43.234509 c
914 | 7.482277 44.307678 10.239509 44.307678 15.753847 44.307678 c
915 | 28.553848 44.307678 l
916 | 34.068188 44.307678 36.825417 44.307678 38.931572 43.234509 c
917 | 40.784248 42.290527 42.290527 40.784256 43.234528 38.931557 c
918 | 44.307697 36.825401 44.307693 34.068169 44.307693 28.553831 c
919 | 44.307693 15.753830 l
920 | 44.307693 10.239491 44.307697 7.482262 43.234528 5.376106 c
921 | 42.290527 3.523430 40.784248 2.017151 38.931572 1.073151 c
922 | 36.825417 -0.000019 34.068188 -0.000015 28.553848 -0.000015 c
923 | 15.753847 -0.000015 l
924 | 10.239509 -0.000015 7.482277 -0.000019 5.376123 1.073151 c
925 | 3.523422 2.017151 2.017151 3.523430 1.073169 5.376106 c
926 | -0.000000 7.482262 0.000000 10.239491 0.000000 15.753830 c
927 | 0.000000 28.553831 l
928 | h
929 | f
930 | n
931 | Q
932 | q
933 | 1.000000 0.000000 -0.000000 1.000000 55.846130 599.846191 cm
934 | 0.000000 0.000000 0.000000 scn
935 | 28.553848 42.461525 m
936 | 15.753847 42.461525 l
937 | 12.966216 42.461525 10.986524 42.460091 9.437232 42.333511 c
938 | 7.909539 42.208694 6.963692 41.971451 6.214215 41.589573 c
939 | 4.708923 40.822590 3.485090 39.598755 2.718105 38.093464 c
940 | 2.336228 37.343987 2.098985 36.398140 1.974166 34.870445 c
941 | 1.847588 33.321156 1.846154 31.341461 1.846154 28.553831 c
942 | 1.846154 15.753830 l
943 | 1.846154 12.966200 1.847588 10.986507 1.974166 9.437214 c
944 | 2.098985 7.909523 2.336228 6.963676 2.718105 6.214199 c
945 | 3.485090 4.708908 4.708923 3.485092 6.214215 2.718079 c
946 | 6.963692 2.336231 7.909539 2.098999 9.437232 1.974140 c
947 | 10.986524 1.847553 12.966216 1.846138 15.753847 1.846138 c
948 | 28.553848 1.846138 l
949 | 31.341478 1.846138 33.321171 1.847553 34.870464 1.974140 c
950 | 36.398155 2.098999 37.344002 2.336231 38.093479 2.718079 c
951 | 39.598770 3.485092 40.822586 4.708908 41.589600 6.214199 c
952 | 41.971447 6.963676 42.208679 7.909523 42.333538 9.437214 c
953 | 42.460125 10.986507 42.461540 12.966200 42.461540 15.753830 c
954 | 42.461540 28.553831 l
955 | 42.461540 31.341461 42.460125 33.321156 42.333538 34.870445 c
956 | 42.208679 36.398140 41.971447 37.343987 41.589600 38.093464 c
957 | 40.822586 39.598755 39.598770 40.822590 38.093479 41.589573 c
958 | 37.344002 41.971451 36.398155 42.208694 34.870464 42.333511 c
959 | 33.321171 42.460091 31.341478 42.461525 28.553848 42.461525 c
960 | h
961 | 1.073169 38.931557 m
962 | -0.000000 36.825401 0.000000 34.068169 0.000000 28.553831 c
963 | 0.000000 15.753830 l
964 | 0.000000 10.239491 -0.000000 7.482262 1.073169 5.376106 c
965 | 2.017151 3.523430 3.523422 2.017151 5.376123 1.073151 c
966 | 7.482277 -0.000019 10.239509 -0.000015 15.753847 -0.000015 c
967 | 28.553848 -0.000015 l
968 | 34.068188 -0.000015 36.825417 -0.000019 38.931572 1.073151 c
969 | 40.784248 2.017151 42.290527 3.523430 43.234528 5.376106 c
970 | 44.307697 7.482262 44.307693 10.239491 44.307693 15.753830 c
971 | 44.307693 28.553831 l
972 | 44.307693 34.068169 44.307697 36.825401 43.234528 38.931557 c
973 | 42.290527 40.784256 40.784248 42.290527 38.931572 43.234509 c
974 | 36.825417 44.307678 34.068188 44.307678 28.553848 44.307678 c
975 | 15.753847 44.307678 l
976 | 10.239509 44.307678 7.482277 44.307678 5.376123 43.234509 c
977 | 3.523422 42.290527 2.017151 40.784256 1.073169 38.931557 c
978 | h
979 | f*
980 | n
981 | Q
982 | q
983 | 1.000000 0.000000 -0.000000 1.000000 55.846130 599.846191 cm
984 | /Pattern cs
985 | /P2 scn
986 | 0.000000 28.553831 m
987 | 0.000000 34.068169 -0.000000 36.825401 1.073169 38.931557 c
988 | 2.017151 40.784256 3.523422 42.290527 5.376123 43.234509 c
989 | 7.482277 44.307678 10.239509 44.307678 15.753847 44.307678 c
990 | 28.553848 44.307678 l
991 | 34.068188 44.307678 36.825417 44.307678 38.931572 43.234509 c
992 | 40.784248 42.290527 42.290527 40.784256 43.234528 38.931557 c
993 | 44.307697 36.825401 44.307693 34.068169 44.307693 28.553831 c
994 | 44.307693 15.753830 l
995 | 44.307693 10.239491 44.307697 7.482262 43.234528 5.376106 c
996 | 42.290527 3.523430 40.784248 2.017151 38.931572 1.073151 c
997 | 36.825417 -0.000019 34.068188 -0.000015 28.553848 -0.000015 c
998 | 15.753847 -0.000015 l
999 | 10.239509 -0.000015 7.482277 -0.000019 5.376123 1.073151 c
1000 | 3.523422 2.017151 2.017151 3.523430 1.073169 5.376106 c
1001 | -0.000000 7.482262 0.000000 10.239491 0.000000 15.753830 c
1002 | 0.000000 28.553831 l
1003 | h
1004 | f
1005 | n
1006 | Q
1007 | q
1008 | 1.000000 0.000000 -0.000000 1.000000 54.000000 598.000000 cm
1009 | 0.000000 0.000000 0.000000 scn
1010 | 30.481234 48.000000 m
1011 | 33.169601 48.000015 35.300064 48.000027 37.017292 47.859722 c
1012 | 38.773293 47.716248 40.259140 47.416904 41.615879 46.725613 c
1013 | 43.815941 45.604633 45.604618 43.815937 46.725601 41.615875 c
1014 | 47.416924 40.259136 47.716251 38.773293 47.859695 37.017292 c
1015 | 48.000004 35.300060 48.000000 33.169601 48.000000 30.481293 c
1016 | 48.000000 17.518705 l
1017 | 48.000000 14.830399 48.000004 12.699936 47.859695 10.982708 c
1018 | 47.716251 9.226707 47.416924 7.740860 46.725601 6.384121 c
1019 | 45.604618 4.184059 43.815941 2.395382 41.615879 1.274399 c
1020 | 40.259140 0.583076 38.773293 0.283749 37.017292 0.140305 c
1021 | 35.300064 -0.000004 33.169601 0.000000 30.481295 0.000000 c
1022 | 17.518707 0.000000 l
1023 | 14.830400 0.000000 12.699940 -0.000004 10.982709 0.140305 c
1024 | 9.226708 0.283749 7.740862 0.583076 6.384123 1.274399 c
1025 | 4.184062 2.395382 2.395366 4.184059 1.274388 6.384121 c
1026 | 0.583094 7.740860 0.283751 9.226707 0.140278 10.982708 c
1027 | -0.000025 12.699936 -0.000014 14.830399 0.000000 17.518766 c
1028 | 0.000000 30.481230 l
1029 | -0.000014 33.169601 -0.000025 35.300060 0.140278 37.017292 c
1030 | 0.283751 38.773293 0.583094 40.259136 1.274388 41.615875 c
1031 | 2.395366 43.815937 4.184062 45.604633 6.384123 46.725613 c
1032 | 7.740862 47.416908 9.226708 47.716248 10.982709 47.859722 c
1033 | 12.699940 48.000027 14.830400 48.000015 17.518770 48.000000 c
1034 | 30.481234 48.000000 l
1035 | h
1036 | 2.919323 40.777721 m
1037 | 1.846154 38.671570 1.846154 35.914337 1.846154 30.400000 c
1038 | 1.846154 17.599998 l
1039 | 1.846154 12.085659 1.846154 9.328430 2.919323 7.222275 c
1040 | 3.863305 5.369598 5.369576 3.863319 7.222277 2.919319 c
1041 | 9.328431 1.846149 12.085662 1.846153 17.600000 1.846153 c
1042 | 30.400002 1.846153 l
1043 | 35.914341 1.846153 38.671570 1.846149 40.777725 2.919319 c
1044 | 42.630402 3.863319 44.136681 5.369598 45.080681 7.222275 c
1045 | 46.153851 9.328430 46.153847 12.085659 46.153847 17.599998 c
1046 | 46.153847 30.400000 l
1047 | 46.153847 35.914337 46.153851 38.671570 45.080681 40.777721 c
1048 | 44.136681 42.630424 42.630402 44.136696 40.777725 45.080677 c
1049 | 38.671570 46.153847 35.914341 46.153847 30.400002 46.153847 c
1050 | 17.600000 46.153847 l
1051 | 12.085662 46.153847 9.328431 46.153847 7.222277 45.080677 c
1052 | 5.369576 44.136696 3.863305 42.630424 2.919323 40.777721 c
1053 | h
1054 | f*
1055 | n
1056 | Q
1057 | q
1058 | /E1 gs
1059 | 1.000000 0.000000 -0.000000 1.000000 54.000000 598.000000 cm
1060 | /Pattern cs
1061 | /P3 scn
1062 | 30.481234 48.000000 m
1063 | 33.169601 48.000015 35.300064 48.000027 37.017292 47.859722 c
1064 | 38.773293 47.716248 40.259140 47.416904 41.615879 46.725613 c
1065 | 43.815941 45.604633 45.604618 43.815937 46.725601 41.615875 c
1066 | 47.416924 40.259136 47.716251 38.773293 47.859695 37.017292 c
1067 | 48.000004 35.300060 48.000000 33.169601 48.000000 30.481293 c
1068 | 48.000000 17.518705 l
1069 | 48.000000 14.830399 48.000004 12.699936 47.859695 10.982708 c
1070 | 47.716251 9.226707 47.416924 7.740860 46.725601 6.384121 c
1071 | 45.604618 4.184059 43.815941 2.395382 41.615879 1.274399 c
1072 | 40.259140 0.583076 38.773293 0.283749 37.017292 0.140305 c
1073 | 35.300064 -0.000004 33.169601 0.000000 30.481295 0.000000 c
1074 | 17.518707 0.000000 l
1075 | 14.830400 0.000000 12.699940 -0.000004 10.982709 0.140305 c
1076 | 9.226708 0.283749 7.740862 0.583076 6.384123 1.274399 c
1077 | 4.184062 2.395382 2.395366 4.184059 1.274388 6.384121 c
1078 | 0.583094 7.740860 0.283751 9.226707 0.140278 10.982708 c
1079 | -0.000025 12.699936 -0.000014 14.830399 0.000000 17.518766 c
1080 | 0.000000 30.481230 l
1081 | -0.000014 33.169601 -0.000025 35.300060 0.140278 37.017292 c
1082 | 0.283751 38.773293 0.583094 40.259136 1.274388 41.615875 c
1083 | 2.395366 43.815937 4.184062 45.604633 6.384123 46.725613 c
1084 | 7.740862 47.416908 9.226708 47.716248 10.982709 47.859722 c
1085 | 12.699940 48.000027 14.830400 48.000015 17.518770 48.000000 c
1086 | 30.481234 48.000000 l
1087 | h
1088 | 2.919323 40.777721 m
1089 | 1.846154 38.671570 1.846154 35.914337 1.846154 30.400000 c
1090 | 1.846154 17.599998 l
1091 | 1.846154 12.085659 1.846154 9.328430 2.919323 7.222275 c
1092 | 3.863305 5.369598 5.369576 3.863319 7.222277 2.919319 c
1093 | 9.328431 1.846149 12.085662 1.846153 17.600000 1.846153 c
1094 | 30.400002 1.846153 l
1095 | 35.914341 1.846153 38.671570 1.846149 40.777725 2.919319 c
1096 | 42.630402 3.863319 44.136681 5.369598 45.080681 7.222275 c
1097 | 46.153851 9.328430 46.153847 12.085659 46.153847 17.599998 c
1098 | 46.153847 30.400000 l
1099 | 46.153847 35.914337 46.153851 38.671570 45.080681 40.777721 c
1100 | 44.136681 42.630424 42.630402 44.136696 40.777725 45.080677 c
1101 | 38.671570 46.153847 35.914341 46.153847 30.400002 46.153847 c
1102 | 17.600000 46.153847 l
1103 | 12.085662 46.153847 9.328431 46.153847 7.222277 45.080677 c
1104 | 5.369576 44.136696 3.863305 42.630424 2.919323 40.777721 c
1105 | h
1106 | f*
1107 | n
1108 | Q
1109 | q
1110 | /E2 gs
1111 | 1.000000 0.000000 -0.000000 1.000000 71.476868 612.707703 cm
1112 | /Pattern cs
1113 | /P4 scn
1114 | 0.000000 -0.000017 m
1115 | 0.000000 19.076904 l
1116 | 7.247755 19.076904 l
1117 | 8.732555 19.076904 9.978277 18.822321 10.984923 18.313089 c
1118 | 11.997847 17.803858 12.762276 17.089705 13.278153 16.170628 c
1119 | 13.800368 15.257766 14.061415 14.192782 14.061415 12.975613 c
1120 | 14.061415 11.752291 13.797171 10.690382 13.268740 9.789951 c
1121 | 12.746524 8.895737 11.975815 8.203305 10.956615 7.712721 c
1122 | 9.937353 7.228352 8.685353 6.986135 7.200614 6.986135 c
1123 | 2.038462 6.986135 l
1124 | 2.038462 9.855121 l
1125 | 6.728738 9.855121 l
1126 | 7.596922 9.855121 8.307877 9.973151 8.861539 10.209089 c
1127 | 9.415200 10.451304 9.824122 10.802135 10.088368 11.261703 c
1128 | 10.358891 11.727427 10.494154 12.298752 10.494154 12.975613 c
1129 | 10.494154 13.652536 10.358891 14.230074 10.088368 14.708227 c
1130 | 9.817845 15.192596 9.405723 15.558997 8.852122 15.807366 c
1131 | 8.298461 16.061951 7.584368 16.189274 6.709845 16.189274 c
1132 | 3.501231 16.189274 l
1133 | 3.501231 -0.000017 l
1134 | 0.000000 -0.000017 l
1135 | h
1136 | 9.984555 8.644228 m
1137 | 14.769230 -0.000017 l
1138 | 10.862215 -0.000017 l
1139 | 6.162524 8.644228 l
1140 | 9.984555 8.644228 l
1141 | h
1142 | f
1143 | n
1144 | Q
1145 | q
1146 | 1.000000 0.000000 -0.000000 1.000000 54.000000 543.594482 cm
1147 | 1.000000 1.000000 1.000000 scn
1148 | 0.000000 -0.594482 m
1149 | h
1150 | 1.291193 -0.594482 m
1151 | 1.291193 12.496428 l
1152 | 6.200284 12.496428 l
1153 | 7.205966 12.496428 8.049716 12.321711 8.731535 11.972280 c
1154 | 9.417615 11.622848 9.935370 11.132791 10.284801 10.502110 c
1155 | 10.638495 9.875689 10.815342 9.144864 10.815342 8.309637 c
1156 | 10.815342 7.470149 10.636365 6.741455 10.278410 6.123557 c
1157 | 9.924716 5.509921 9.402699 5.034780 8.712358 4.698132 c
1158 | 8.022017 4.365746 7.174006 4.199553 6.168324 4.199553 c
1159 | 2.671875 4.199553 l
1160 | 2.671875 6.168303 l
1161 | 5.848722 6.168303 l
1162 | 6.436790 6.168303 6.918324 6.249269 7.293324 6.411200 c
1163 | 7.668324 6.577394 7.945313 6.818160 8.124290 7.133501 c
1164 | 8.307528 7.453103 8.399148 7.845149 8.399148 8.309637 c
1165 | 8.399148 8.774125 8.307528 9.170432 8.124290 9.498558 c
1166 | 7.941051 9.830944 7.661932 10.082365 7.286932 10.252819 c
1167 | 6.911932 10.427534 6.428267 10.514893 5.835938 10.514893 c
1168 | 3.662642 10.514893 l
1169 | 3.662642 -0.594482 l
1170 | 1.291193 -0.594482 l
1171 | h
1172 | 8.053977 5.337336 m
1173 | 11.294744 -0.594482 l
1174 | 8.648438 -0.594482 l
1175 | 5.465199 5.337336 l
1176 | 8.053977 5.337336 l
1177 | h
1178 | 11.566406 -0.594482 m
1179 | h
1180 | 17.101917 -0.786243 m
1181 | 16.117542 -0.786243 15.267401 -0.581697 14.551492 -0.172607 c
1182 | 13.839844 0.240744 13.292259 0.824551 12.908736 1.578814 c
1183 | 12.525213 2.337337 12.333451 3.230092 12.333451 4.257080 c
1184 | 12.333451 5.267023 12.525213 6.153387 12.908736 6.916171 c
1185 | 13.296520 7.683217 13.837713 8.279808 14.532315 8.705944 c
1186 | 15.226917 9.136342 16.042969 9.351541 16.980469 9.351541 c
1187 | 17.585583 9.351541 18.156605 9.253530 18.693537 9.057507 c
1188 | 19.234730 8.865746 19.712004 8.567450 20.125355 8.162620 c
1189 | 20.542969 7.757791 20.871094 7.242166 21.109730 6.615745 c
1190 | 21.348368 5.993586 21.467686 5.252109 21.467686 4.391314 c
1191 | 21.467686 3.681796 l
1192 | 13.420099 3.681796 l
1193 | 13.420099 5.241455 l
1194 | 19.249645 5.241455 l
1195 | 19.245384 5.684637 19.149504 6.078814 18.962004 6.423984 c
1196 | 18.774504 6.773416 18.512430 7.048273 18.175781 7.248557 c
1197 | 17.843395 7.448841 17.455610 7.548984 17.012428 7.548984 c
1198 | 16.539417 7.548984 16.123936 7.433927 15.765981 7.203814 c
1199 | 15.408026 6.977962 15.128906 6.679666 14.928622 6.308928 c
1200 | 14.732599 5.942450 14.632458 5.539750 14.628196 5.100830 c
1201 | 14.628196 3.739325 l
1202 | 14.628196 3.168302 14.732599 2.678245 14.941406 2.269154 c
1203 | 15.150213 1.864325 15.442117 1.553245 15.817117 1.335915 c
1204 | 16.192116 1.122847 16.631037 1.016314 17.133879 1.016314 c
1205 | 17.470526 1.016314 17.775213 1.063189 18.047941 1.156939 c
1206 | 18.320667 1.254950 18.557173 1.397705 18.757458 1.585205 c
1207 | 18.957743 1.772705 19.109020 2.004950 19.211292 2.281939 c
1208 | 21.371803 2.039041 l
1209 | 21.235441 1.468018 20.975498 0.969439 20.591976 0.543303 c
1210 | 20.212713 0.121428 19.726917 -0.206697 19.134588 -0.441072 c
1211 | 18.542259 -0.671186 17.864702 -0.786243 17.101917 -0.786243 c
1212 | h
1213 | 22.236328 -0.594482 m
1214 | h
1215 | 27.707920 -0.786243 m
1216 | 26.727806 -0.786243 25.886187 -0.571045 25.183062 -0.140648 c
1217 | 24.484198 0.289751 23.945135 0.884211 23.565874 1.642734 c
1218 | 23.190874 2.405518 23.003374 3.283359 23.003374 4.276257 c
1219 | 23.003374 5.273416 23.195135 6.153387 23.578657 6.916171 c
1220 | 23.962181 7.683217 24.503374 8.279808 25.202238 8.705944 c
1221 | 25.905363 9.136342 26.736328 9.351541 27.695135 9.351541 c
1222 | 28.492010 9.351541 29.197266 9.204524 29.810902 8.910490 c
1223 | 30.428801 8.620717 30.920988 8.209495 31.287464 7.676825 c
1224 | 31.653942 7.148416 31.862749 6.530518 31.913885 5.823132 c
1225 | 29.702238 5.823132 l
1226 | 29.612749 6.296143 29.399681 6.690319 29.063032 7.005660 c
1227 | 28.730646 7.325262 28.285334 7.485064 27.727095 7.485064 c
1228 | 27.254084 7.485064 26.838600 7.357223 26.480646 7.101541 c
1229 | 26.122692 6.850121 25.843573 6.487905 25.643288 6.014893 c
1230 | 25.447266 5.541882 25.349255 4.975121 25.349255 4.314609 c
1231 | 25.349255 3.645575 25.447266 3.070291 25.643288 2.588757 c
1232 | 25.839312 2.111484 26.114170 1.742876 26.467863 1.482932 c
1233 | 26.825817 1.227249 27.245562 1.099409 27.727095 1.099409 c
1234 | 28.068005 1.099409 28.372692 1.163330 28.641157 1.291170 c
1235 | 28.913885 1.423273 29.141869 1.612905 29.325108 1.860064 c
1236 | 29.508345 2.107223 29.634056 2.407648 29.702238 2.761342 c
1237 | 31.913885 2.761342 l
1238 | 31.858486 2.066740 31.653942 1.450973 31.300249 0.914040 c
1239 | 30.946556 0.381369 30.465021 -0.036243 29.855646 -0.338800 c
1240 | 29.246271 -0.637096 28.530363 -0.786243 27.707920 -0.786243 c
1241 | h
1242 | 32.625000 -0.594482 m
1243 | h
1244 | 38.160511 -0.786243 m
1245 | 37.176136 -0.786243 36.325993 -0.581697 35.610085 -0.172607 c
1246 | 34.898438 0.240744 34.350853 0.824551 33.967331 1.578814 c
1247 | 33.583805 2.337337 33.392044 3.230092 33.392044 4.257080 c
1248 | 33.392044 5.267023 33.583805 6.153387 33.967331 6.916171 c
1249 | 34.355114 7.683217 34.896309 8.279808 35.590908 8.705944 c
1250 | 36.285511 9.136342 37.101562 9.351541 38.039062 9.351541 c
1251 | 38.644176 9.351541 39.215199 9.253530 39.752132 9.057507 c
1252 | 40.293324 8.865746 40.770596 8.567450 41.183949 8.162620 c
1253 | 41.601562 7.757791 41.929688 7.242166 42.168324 6.615745 c
1254 | 42.406960 5.993586 42.526279 5.252109 42.526279 4.391314 c
1255 | 42.526279 3.681796 l
1256 | 34.478695 3.681796 l
1257 | 34.478695 5.241455 l
1258 | 40.308239 5.241455 l
1259 | 40.303978 5.684637 40.208096 6.078814 40.020596 6.423984 c
1260 | 39.833096 6.773416 39.571022 7.048273 39.234375 7.248557 c
1261 | 38.901989 7.448841 38.514202 7.548984 38.071022 7.548984 c
1262 | 37.598011 7.548984 37.182529 7.433927 36.824574 7.203814 c
1263 | 36.466621 6.977962 36.187500 6.679666 35.987217 6.308928 c
1264 | 35.791191 5.942450 35.691051 5.539750 35.686790 5.100830 c
1265 | 35.686790 3.739325 l
1266 | 35.686790 3.168302 35.791195 2.678245 36.000000 2.269154 c
1267 | 36.208805 1.864325 36.500710 1.553245 36.875710 1.335915 c
1268 | 37.250710 1.122847 37.689629 1.016314 38.192471 1.016314 c
1269 | 38.529121 1.016314 38.833809 1.063189 39.106533 1.156939 c
1270 | 39.379261 1.254950 39.615768 1.397705 39.816051 1.585205 c
1271 | 40.016335 1.772705 40.167614 2.004950 40.269886 2.281939 c
1272 | 42.430397 2.039041 l
1273 | 42.294033 1.468018 42.034092 0.969439 41.650570 0.543303 c
1274 | 41.271309 0.121428 40.785511 -0.206697 40.193184 -0.441072 c
1275 | 39.600853 -0.671186 38.923294 -0.786243 38.160511 -0.786243 c
1276 | h
1277 | 43.294922 -0.594482 m
1278 | h
1279 | 44.483841 -0.594482 m
1280 | 44.483841 9.223700 l
1281 | 46.797764 9.223700 l
1282 | 46.797764 -0.594482 l
1283 | 44.483841 -0.594482 l
1284 | h
1285 | 45.647194 10.617165 m
1286 | 45.280716 10.617165 44.965374 10.738614 44.701172 10.981512 c
1287 | 44.436970 11.228672 44.304867 11.524837 44.304867 11.870007 c
1288 | 44.304867 12.219439 44.436970 12.515604 44.701172 12.758501 c
1289 | 44.965374 13.005660 45.280716 13.129240 45.647194 13.129240 c
1290 | 46.017933 13.129240 46.333275 13.005660 46.593216 12.758501 c
1291 | 46.857422 12.515604 46.989525 12.219439 46.989525 11.870007 c
1292 | 46.989525 11.524837 46.857422 11.228672 46.593216 10.981512 c
1293 | 46.333275 10.738614 46.017933 10.617165 45.647194 10.617165 c
1294 | h
1295 | 47.988281 -0.594482 m
1296 | h
1297 | 49.177200 -4.276300 m
1298 | 49.177200 9.223700 l
1299 | 51.452770 9.223700 l
1300 | 51.452770 7.600120 l
1301 | 51.587002 7.600120 l
1302 | 51.706322 7.838757 51.874645 8.092307 52.091976 8.360773 c
1303 | 52.309303 8.633501 52.603336 8.865746 52.974075 9.057507 c
1304 | 53.344814 9.253530 53.817829 9.351541 54.393112 9.351541 c
1305 | 55.151634 9.351541 55.835583 9.157649 56.444958 8.769865 c
1306 | 57.058594 8.386342 57.544392 7.817450 57.902344 7.063189 c
1307 | 58.264561 6.313189 58.445667 5.392734 58.445667 4.301825 c
1308 | 58.445667 3.223700 58.268822 2.307507 57.915127 1.553245 c
1309 | 57.561436 0.798983 57.079903 0.223700 56.470528 -0.172607 c
1310 | 55.861153 -0.568914 55.170811 -0.767067 54.399502 -0.767067 c
1311 | 53.837002 -0.767067 53.370384 -0.673317 52.999645 -0.485817 c
1312 | 52.628906 -0.298317 52.330612 -0.072464 52.104759 0.191740 c
1313 | 51.883167 0.460205 51.710583 0.713757 51.587002 0.952393 c
1314 | 51.491123 0.952393 l
1315 | 51.491123 -4.276300 l
1316 | 49.177200 -4.276300 l
1317 | h
1318 | 51.446377 4.314609 m
1319 | 51.446377 3.679666 51.535866 3.123558 51.714844 2.646285 c
1320 | 51.898083 2.169012 52.160156 1.796143 52.501064 1.527677 c
1321 | 52.846237 1.263474 53.263851 1.131371 53.753906 1.131371 c
1322 | 54.265270 1.131371 54.693535 1.267735 55.038708 1.540461 c
1323 | 55.383881 1.817450 55.643822 2.194580 55.818539 2.671853 c
1324 | 55.997513 3.153387 56.087002 3.700973 56.087002 4.314609 c
1325 | 56.087002 4.923984 55.999645 5.465177 55.824928 5.938189 c
1326 | 55.650211 6.411200 55.390270 6.781939 55.045101 7.050405 c
1327 | 54.699928 7.318871 54.269531 7.453103 53.753906 7.453103 c
1328 | 53.259586 7.453103 52.839844 7.323132 52.494675 7.063189 c
1329 | 52.149502 6.803245 51.887428 6.438898 51.708450 5.970148 c
1330 | 51.533733 5.501398 51.446377 4.949552 51.446377 4.314609 c
1331 | h
1332 | 59.238281 -0.594482 m
1333 | h
1334 | 65.329903 9.223700 m
1335 | 65.329903 7.433928 l
1336 | 59.685726 7.433928 l
1337 | 59.685726 9.223700 l
1338 | 65.329903 9.223700 l
1339 | h
1340 | 61.079189 11.575973 m
1341 | 63.393112 11.575973 l
1342 | 63.393112 2.358643 l
1343 | 63.393112 2.047563 63.439987 1.808927 63.533737 1.642734 c
1344 | 63.631748 1.480802 63.759586 1.370007 63.917259 1.310347 c
1345 | 64.074928 1.250687 64.249641 1.220858 64.441406 1.220858 c
1346 | 64.586296 1.220858 64.718399 1.231512 64.837715 1.252819 c
1347 | 64.961288 1.274126 65.055038 1.293301 65.118965 1.310347 c
1348 | 65.508881 -0.498602 l
1349 | 65.385300 -0.541216 65.208450 -0.588091 64.978340 -0.639227 c
1350 | 64.752487 -0.690363 64.475494 -0.720192 64.147369 -0.728716 c
1351 | 63.567825 -0.745762 63.045811 -0.658403 62.581322 -0.466642 c
1352 | 62.116833 -0.270618 61.748222 0.031939 61.475498 0.441029 c
1353 | 61.207031 0.850119 61.074928 1.361483 61.079189 1.975120 c
1354 | 61.079189 11.575973 l
1355 | h
1356 | 70.541016 -0.594482 m
1357 | h
1358 | 76.702950 9.223700 m
1359 | 76.702950 7.433928 l
1360 | 70.898972 7.433928 l
1361 | 70.898972 9.223700 l
1362 | 76.702950 9.223700 l
1363 | h
1364 | 72.349968 -0.594482 m
1365 | 72.349968 10.150547 l
1366 | 72.349968 10.811058 72.486328 11.360774 72.759056 11.799694 c
1367 | 73.036041 12.238615 73.406784 12.566740 73.871269 12.784069 c
1368 | 74.335754 13.001399 74.851379 13.110064 75.418144 13.110064 c
1369 | 75.818710 13.110064 76.174538 13.078104 76.485619 13.014183 c
1370 | 76.796700 12.950262 77.026810 12.892735 77.175957 12.841598 c
1371 | 76.715729 11.051825 l
1372 | 76.617722 11.081654 76.494141 11.111485 76.344994 11.141314 c
1373 | 76.195847 11.175405 76.029648 11.192450 75.846413 11.192450 c
1374 | 75.416016 11.192450 75.111328 11.088046 74.932350 10.879240 c
1375 | 74.757637 10.674694 74.670280 10.380660 74.670280 9.997137 c
1376 | 74.670280 -0.594482 l
1377 | 72.349968 -0.594482 l
1378 | h
1379 | 76.957031 -0.594482 m
1380 | h
1381 | 82.428619 -0.786243 m
1382 | 81.469810 -0.786243 80.638847 -0.575306 79.935722 -0.153431 c
1383 | 79.232597 0.268444 78.687141 0.858643 78.299362 1.617166 c
1384 | 77.915840 2.375689 77.724075 3.262052 77.724075 4.276257 c
1385 | 77.724075 5.290462 77.915840 6.178956 78.299362 6.941740 c
1386 | 78.687141 7.704524 79.232597 8.296853 79.935722 8.718728 c
1387 | 80.638847 9.140603 81.469810 9.351541 82.428619 9.351541 c
1388 | 83.387428 9.351541 84.218391 9.140603 84.921516 8.718728 c
1389 | 85.624649 8.296853 86.167969 7.704524 86.551491 6.941740 c
1390 | 86.939278 6.178956 87.133171 5.290462 87.133171 4.276257 c
1391 | 87.133171 3.262052 86.939278 2.375689 86.551491 1.617166 c
1392 | 86.167969 0.858643 85.624649 0.268444 84.921516 -0.153431 c
1393 | 84.218391 -0.575306 83.387428 -0.786243 82.428619 -0.786243 c
1394 | h
1395 | 82.441406 1.067450 m
1396 | 82.961296 1.067450 83.395950 1.210205 83.745384 1.495716 c
1397 | 84.094818 1.785489 84.354759 2.173274 84.525215 2.659069 c
1398 | 84.699928 3.144864 84.787285 3.686057 84.787285 4.282648 c
1399 | 84.787285 4.883501 84.699928 5.426825 84.525215 5.912620 c
1400 | 84.354759 6.402677 84.094818 6.792592 83.745384 7.082365 c
1401 | 83.395950 7.372137 82.961296 7.517023 82.441406 7.517023 c
1402 | 81.908737 7.517023 81.465553 7.372137 81.111862 7.082365 c
1403 | 80.762428 6.792592 80.500359 6.402677 80.325638 5.912620 c
1404 | 80.155182 5.426825 80.069954 4.883501 80.069954 4.282648 c
1405 | 80.069954 3.686057 80.155182 3.144864 80.325638 2.659069 c
1406 | 80.500359 2.173274 80.762428 1.785489 81.111862 1.495716 c
1407 | 81.465553 1.210205 81.908737 1.067450 82.441406 1.067450 c
1408 | h
1409 | 87.908203 -0.594482 m
1410 | h
1411 | 89.097122 -0.594482 m
1412 | 89.097122 9.223700 l
1413 | 91.340729 9.223700 l
1414 | 91.340729 7.587336 l
1415 | 91.443001 7.587336 l
1416 | 91.621979 8.154098 91.928802 8.590887 92.363457 8.897705 c
1417 | 92.802383 9.208785 93.303093 9.364325 93.865593 9.364325 c
1418 | 93.993431 9.364325 94.136185 9.357933 94.293854 9.345149 c
1419 | 94.455788 9.336626 94.590019 9.321712 94.696556 9.300405 c
1420 | 94.696556 7.171853 l
1421 | 94.598541 7.205944 94.443001 7.235774 94.229935 7.261342 c
1422 | 94.021126 7.291171 93.818710 7.306086 93.622688 7.306086 c
1423 | 93.200813 7.306086 92.821556 7.214466 92.484909 7.031228 c
1424 | 92.152519 6.852251 91.890450 6.602961 91.698685 6.283359 c
1425 | 91.506920 5.963757 91.411041 5.595148 91.411041 5.177535 c
1426 | 91.411041 -0.594482 l
1427 | 89.097122 -0.594482 l
1428 | h
1429 | 99.509766 -0.594482 m
1430 | h
1431 | 99.906075 12.496428 m
1432 | 102.584343 12.496428 l
1433 | 105.786751 6.705234 l
1434 | 105.914597 6.705234 l
1435 | 109.117012 12.496428 l
1436 | 111.795280 12.496428 l
1437 | 107.033203 4.289041 l
1438 | 107.033203 -0.594482 l
1439 | 104.668144 -0.594482 l
1440 | 104.668144 4.289041 l
1441 | 99.906075 12.496428 l
1442 | h
1443 | 110.355469 -0.594482 m
1444 | h
1445 | 115.827057 -0.786243 m
1446 | 114.868248 -0.786243 114.037285 -0.575306 113.334160 -0.153431 c
1447 | 112.631035 0.268444 112.085579 0.858643 111.697800 1.617166 c
1448 | 111.314278 2.375689 111.122513 3.262052 111.122513 4.276257 c
1449 | 111.122513 5.290462 111.314278 6.178956 111.697800 6.941740 c
1450 | 112.085579 7.704524 112.631035 8.296853 113.334160 8.718728 c
1451 | 114.037285 9.140603 114.868248 9.351541 115.827057 9.351541 c
1452 | 116.785866 9.351541 117.616829 9.140603 118.319954 8.718728 c
1453 | 119.023087 8.296853 119.566406 7.704524 119.949928 6.941740 c
1454 | 120.337715 6.178956 120.531609 5.290462 120.531609 4.276257 c
1455 | 120.531609 3.262052 120.337715 2.375689 119.949928 1.617166 c
1456 | 119.566406 0.858643 119.023087 0.268444 118.319954 -0.153431 c
1457 | 117.616829 -0.575306 116.785866 -0.786243 115.827057 -0.786243 c
1458 | h
1459 | 115.839844 1.067450 m
1460 | 116.359734 1.067450 116.794388 1.210205 117.143822 1.495716 c
1461 | 117.493256 1.785489 117.753197 2.173274 117.923653 2.659069 c
1462 | 118.098366 3.144864 118.185722 3.686057 118.185722 4.282648 c
1463 | 118.185722 4.883501 118.098366 5.426825 117.923653 5.912620 c
1464 | 117.753197 6.402677 117.493256 6.792592 117.143822 7.082365 c
1465 | 116.794388 7.372137 116.359734 7.517023 115.839844 7.517023 c
1466 | 115.307175 7.517023 114.863991 7.372137 114.510300 7.082365 c
1467 | 114.160866 6.792592 113.898796 6.402677 113.724075 5.912620 c
1468 | 113.553619 5.426825 113.468391 4.883501 113.468391 4.282648 c
1469 | 113.468391 3.686057 113.553619 3.144864 113.724075 2.659069 c
1470 | 113.898796 2.173274 114.160866 1.785489 114.510300 1.495716 c
1471 | 114.863991 1.210205 115.307175 1.067450 115.839844 1.067450 c
1472 | h
1473 | 121.306641 -0.594482 m
1474 | h
1475 | 128.753372 3.534780 m
1476 | 128.753372 9.223700 l
1477 | 131.067291 9.223700 l
1478 | 131.067291 -0.594482 l
1479 | 128.823685 -0.594482 l
1480 | 128.823685 1.150545 l
1481 | 128.721420 1.150545 l
1482 | 128.499817 0.600830 128.135468 0.151257 127.628372 -0.198175 c
1483 | 127.125534 -0.547607 126.505508 -0.722322 125.768288 -0.722322 c
1484 | 125.124825 -0.722322 124.555931 -0.579567 124.061615 -0.294056 c
1485 | 123.571556 -0.004284 123.188034 0.415461 122.911041 0.965178 c
1486 | 122.634056 1.519155 122.495560 2.188189 122.495560 2.972280 c
1487 | 122.495560 9.223700 l
1488 | 124.809479 9.223700 l
1489 | 124.809479 3.330234 l
1490 | 124.809479 2.708075 124.979935 2.213756 125.320847 1.847279 c
1491 | 125.661751 1.480802 126.109200 1.297564 126.663177 1.297564 c
1492 | 127.004082 1.297564 127.334335 1.380661 127.653938 1.546853 c
1493 | 127.973541 1.713046 128.235626 1.960205 128.440170 2.288330 c
1494 | 128.648972 2.620717 128.753372 3.036200 128.753372 3.534780 c
1495 | h
1496 | 132.257812 -0.594482 m
1497 | h
1498 | 133.446732 -0.594482 m
1499 | 133.446732 9.223700 l
1500 | 135.690338 9.223700 l
1501 | 135.690338 7.587336 l
1502 | 135.792618 7.587336 l
1503 | 135.971588 8.154098 136.278412 8.590887 136.713074 8.897705 c
1504 | 137.151993 9.208785 137.652695 9.364325 138.215195 9.364325 c
1505 | 138.343048 9.364325 138.485794 9.357933 138.643463 9.345149 c
1506 | 138.805405 9.336626 138.939636 9.321712 139.046158 9.300405 c
1507 | 139.046158 7.171853 l
1508 | 138.948151 7.205944 138.792618 7.235774 138.579544 7.261342 c
1509 | 138.370743 7.291171 138.168335 7.306086 137.972305 7.306086 c
1510 | 137.550430 7.306086 137.171173 7.214466 136.834518 7.031228 c
1511 | 136.502136 6.852251 136.240051 6.602961 136.048294 6.283359 c
1512 | 135.856537 5.963757 135.760651 5.595148 135.760651 5.177535 c
1513 | 135.760651 -0.594482 l
1514 | 133.446732 -0.594482 l
1515 | h
1516 | 143.859375 -0.594482 m
1517 | h
1518 | 145.150574 -0.594482 m
1519 | 145.150574 12.496428 l
1520 | 150.059662 12.496428 l
1521 | 151.065338 12.496428 151.909088 12.308928 152.590912 11.933928 c
1522 | 153.276993 11.558928 153.794739 11.043303 154.144180 10.387053 c
1523 | 154.497864 9.735064 154.674713 8.993586 154.674713 8.162620 c
1524 | 154.674713 7.323132 154.497864 6.577393 154.144180 5.925404 c
1525 | 153.790482 5.273416 153.268463 4.759921 152.578125 4.384921 c
1526 | 151.887787 4.014183 151.037643 3.828814 150.027695 3.828814 c
1527 | 146.774155 3.828814 l
1528 | 146.774155 5.778387 l
1529 | 149.708099 5.778387 l
1530 | 150.296158 5.778387 150.777695 5.880660 151.152695 6.085205 c
1531 | 151.527695 6.289750 151.804688 6.571000 151.983658 6.928955 c
1532 | 152.166901 7.286910 152.258530 7.698132 152.258530 8.162620 c
1533 | 152.258530 8.627109 152.166901 9.036200 151.983658 9.389893 c
1534 | 151.804688 9.743586 151.525558 10.018444 151.146301 10.214467 c
1535 | 150.771301 10.414751 150.287643 10.514893 149.695312 10.514893 c
1536 | 147.522018 10.514893 l
1537 | 147.522018 -0.594482 l
1538 | 145.150574 -0.594482 l
1539 | h
1540 | 155.267578 -0.594482 m
1541 | h
1542 | 159.281784 -0.792635 m
1543 | 158.659622 -0.792635 158.099258 -0.681839 157.600677 -0.460249 c
1544 | 157.106354 -0.234398 156.714310 0.097988 156.424545 0.536909 c
1545 | 156.139038 0.975830 155.996277 1.517023 155.996277 2.160489 c
1546 | 155.996277 2.714466 156.098541 3.172564 156.303085 3.534780 c
1547 | 156.507629 3.896996 156.786758 4.186768 157.140442 4.404098 c
1548 | 157.494141 4.621427 157.892578 4.785489 158.335754 4.896285 c
1549 | 158.783203 5.011341 159.245560 5.094438 159.722839 5.145575 c
1550 | 160.298111 5.205234 160.764740 5.258501 161.122696 5.305376 c
1551 | 161.480652 5.356512 161.740585 5.433217 161.902527 5.535490 c
1552 | 162.068710 5.642024 162.151810 5.806087 162.151810 6.027678 c
1553 | 162.151810 6.066029 l
1554 | 162.151810 6.547563 162.009048 6.920432 161.723541 7.184637 c
1555 | 161.438034 7.448842 161.026810 7.580944 160.489883 7.580944 c
1556 | 159.923126 7.580944 159.473541 7.457364 159.141159 7.210205 c
1557 | 158.813034 6.963046 158.591446 6.671143 158.476379 6.334495 c
1558 | 156.315872 6.641314 l
1559 | 156.486328 7.237905 156.767578 7.736484 157.159622 8.137053 c
1560 | 157.551666 8.541883 158.031082 8.844439 158.597839 9.044723 c
1561 | 159.164597 9.249268 159.791016 9.351541 160.477097 9.351541 c
1562 | 160.950104 9.351541 161.420990 9.296143 161.889740 9.185348 c
1563 | 162.358490 9.074553 162.786758 8.891314 163.174545 8.635632 c
1564 | 163.562317 8.384212 163.873398 8.041171 164.107773 7.606512 c
1565 | 164.346405 7.171853 164.465729 6.628529 164.465729 5.976541 c
1566 | 164.465729 -0.594482 l
1567 | 162.241302 -0.594482 l
1568 | 162.241302 0.754240 l
1569 | 162.164597 0.754240 l
1570 | 162.023972 0.481512 161.825821 0.225830 161.570129 -0.012806 c
1571 | 161.318710 -0.247181 161.001251 -0.436811 160.617722 -0.581697 c
1572 | 160.238464 -0.722322 159.793152 -0.792635 159.281784 -0.792635 c
1573 | h
1574 | 159.882629 0.907648 m
1575 | 160.347122 0.907648 160.749817 0.999268 161.090729 1.182507 c
1576 | 161.431641 1.370007 161.693710 1.617166 161.876953 1.923984 c
1577 | 162.064453 2.230802 162.158203 2.565319 162.158203 2.927535 c
1578 | 162.158203 4.084495 l
1579 | 162.085770 4.024836 161.962189 3.969439 161.787460 3.918303 c
1580 | 161.617004 3.867166 161.425247 3.822421 161.212173 3.784069 c
1581 | 160.999115 3.745717 160.788177 3.711626 160.579361 3.681796 c
1582 | 160.370560 3.651966 160.189453 3.626398 160.036041 3.605092 c
1583 | 159.690872 3.558217 159.381927 3.481512 159.109192 3.374978 c
1584 | 158.836472 3.268444 158.621277 3.119296 158.463608 2.927535 c
1585 | 158.305939 2.740035 158.227097 2.497137 158.227097 2.198841 c
1586 | 158.227097 1.772705 158.382629 1.450973 158.693710 1.233643 c
1587 | 159.004791 1.016314 159.401108 0.907648 159.882629 0.907648 c
1588 | h
1589 | 165.251953 -0.594482 m
1590 | h
1591 | 167.879089 -4.276300 m
1592 | 167.563736 -4.276300 167.271835 -4.250732 167.003372 -4.199596 c
1593 | 166.739166 -4.152721 166.528229 -4.097324 166.370560 -4.033403 c
1594 | 166.907486 -2.230846 l
1595 | 167.244141 -2.328857 167.544571 -2.375732 167.808777 -2.371471 c
1596 | 168.072983 -2.367210 168.305222 -2.284113 168.505508 -2.122181 c
1597 | 168.710052 -1.964510 168.882629 -1.700306 169.023254 -1.329567 c
1598 | 169.221420 -0.799028 l
1599 | 165.661041 9.223700 l
1600 | 168.115585 9.223700 l
1601 | 170.378372 1.808927 l
1602 | 170.480652 1.808927 l
1603 | 172.749817 9.223700 l
1604 | 175.210754 9.223700 l
1605 | 171.279648 -1.783403 l
1606 | 171.096405 -2.303289 170.853516 -2.748602 170.550964 -3.119341 c
1607 | 170.248398 -3.494341 169.877655 -3.779852 169.438736 -3.975874 c
1608 | 169.004089 -4.176159 168.484207 -4.276300 167.879089 -4.276300 c
1609 | h
1610 | 175.623047 -0.594482 m
1611 | h
1612 | 176.811966 -0.594482 m
1613 | 176.811966 9.223700 l
1614 | 179.023621 9.223700 l
1615 | 179.023621 7.555376 l
1616 | 179.138672 7.555376 l
1617 | 179.343216 8.117876 179.682007 8.556796 180.155014 8.872137 c
1618 | 180.628021 9.191739 181.192642 9.351541 181.848892 9.351541 c
1619 | 182.513672 9.351541 183.074036 9.189609 183.530014 8.865745 c
1620 | 183.990234 8.546143 184.314102 8.109353 184.501602 7.555376 c
1621 | 184.603867 7.555376 l
1622 | 184.821198 8.100830 185.187683 8.535489 185.703308 8.859353 c
1623 | 186.223190 9.187478 186.838959 9.351541 187.550598 9.351541 c
1624 | 188.454010 9.351541 189.191223 9.066030 189.762253 8.495007 c
1625 | 190.333282 7.923984 190.618790 7.090887 190.618790 5.995716 c
1626 | 190.618790 -0.594482 l
1627 | 188.298477 -0.594482 l
1628 | 188.298477 5.637762 l
1629 | 188.298477 6.247137 188.136551 6.692450 187.812683 6.973700 c
1630 | 187.488815 7.259212 187.092514 7.401967 186.623764 7.401967 c
1631 | 186.065521 7.401967 185.628723 7.227251 185.313385 6.877819 c
1632 | 185.002304 6.532649 184.846771 6.083075 184.846771 5.529098 c
1633 | 184.846771 -0.594482 l
1634 | 182.577591 -0.594482 l
1635 | 182.577591 5.733643 l
1636 | 182.577591 6.240745 182.424179 6.645575 182.117371 6.948132 c
1637 | 181.814804 7.250689 181.418503 7.401967 180.928452 7.401967 c
1638 | 180.596069 7.401967 180.293503 7.316740 180.020767 7.146285 c
1639 | 179.748047 6.980091 179.530716 6.743586 179.368790 6.436768 c
1640 | 179.206848 6.134211 179.125885 5.780518 179.125885 5.375689 c
1641 | 179.125885 -0.594482 l
1642 | 176.811966 -0.594482 l
1643 | h
1644 | 191.777344 -0.594482 m
1645 | h
1646 | 197.312851 -0.786243 m
1647 | 196.328476 -0.786243 195.478333 -0.581697 194.762436 -0.172607 c
1648 | 194.050781 0.240744 193.503204 0.824551 193.119675 1.578814 c
1649 | 192.736145 2.337337 192.544388 3.230092 192.544388 4.257080 c
1650 | 192.544388 5.267023 192.736145 6.153387 193.119675 6.916171 c
1651 | 193.507462 7.683217 194.048660 8.279808 194.743256 8.705944 c
1652 | 195.437851 9.136342 196.253906 9.351541 197.191406 9.351541 c
1653 | 197.796524 9.351541 198.367538 9.253530 198.904480 9.057507 c
1654 | 199.445663 8.865746 199.922943 8.567450 200.336288 8.162620 c
1655 | 200.753906 7.757791 201.082031 7.242166 201.320663 6.615745 c
1656 | 201.559296 5.993586 201.678619 5.252109 201.678619 4.391314 c
1657 | 201.678619 3.681796 l
1658 | 193.631042 3.681796 l
1659 | 193.631042 5.241455 l
1660 | 199.460587 5.241455 l
1661 | 199.456329 5.684637 199.360443 6.078814 199.172943 6.423984 c
1662 | 198.985443 6.773416 198.723373 7.048273 198.386719 7.248557 c
1663 | 198.054337 7.448841 197.666550 7.548984 197.223373 7.548984 c
1664 | 196.750366 7.548984 196.334869 7.433927 195.976913 7.203814 c
1665 | 195.618958 6.977962 195.339844 6.679666 195.139557 6.308928 c
1666 | 194.943527 5.942450 194.843384 5.539750 194.839127 5.100830 c
1667 | 194.839127 3.739325 l
1668 | 194.839127 3.168302 194.943527 2.678245 195.152344 2.269154 c
1669 | 195.361160 1.864325 195.653061 1.553245 196.028061 1.335915 c
1670 | 196.403061 1.122847 196.841980 1.016314 197.344818 1.016314 c
1671 | 197.681473 1.016314 197.986160 1.063189 198.258881 1.156939 c
1672 | 198.531601 1.254950 198.768112 1.397705 198.968399 1.585205 c
1673 | 199.168671 1.772705 199.319946 2.004950 199.422226 2.281939 c
1674 | 201.582748 2.039041 l
1675 | 201.446381 1.468018 201.186432 0.969439 200.802917 0.543303 c
1676 | 200.423660 0.121428 199.937851 -0.206697 199.345520 -0.441072 c
1677 | 198.753189 -0.671186 198.075638 -0.786243 197.312851 -0.786243 c
1678 | h
1679 | 202.447266 -0.594482 m
1680 | h
1681 | 205.950104 5.158359 m
1682 | 205.950104 -0.594482 l
1683 | 203.636185 -0.594482 l
1684 | 203.636185 9.223700 l
1685 | 205.847839 9.223700 l
1686 | 205.847839 7.555376 l
1687 | 205.962891 7.555376 l
1688 | 206.188736 8.105092 206.548828 8.541882 207.043152 8.865745 c
1689 | 207.541733 9.189609 208.157486 9.351541 208.890442 9.351541 c
1690 | 209.568008 9.351541 210.158203 9.206655 210.661041 8.916882 c
1691 | 211.168137 8.627109 211.560181 8.207364 211.837173 7.657648 c
1692 | 212.118423 7.107932 212.256927 6.441030 212.252670 5.656939 c
1693 | 212.252670 -0.594482 l
1694 | 209.938736 -0.594482 l
1695 | 209.938736 5.298984 l
1696 | 209.938736 5.955234 209.768280 6.468728 209.427383 6.839467 c
1697 | 209.090729 7.210205 208.624115 7.395575 208.027527 7.395575 c
1698 | 207.622696 7.395575 207.262604 7.306086 206.947266 7.127109 c
1699 | 206.636185 6.952393 206.391159 6.698842 206.212173 6.366455 c
1700 | 206.037460 6.034069 205.950104 5.631371 205.950104 5.158359 c
1701 | h
1702 | 213.416016 -0.594482 m
1703 | h
1704 | 219.507629 9.223700 m
1705 | 219.507629 7.433928 l
1706 | 213.863464 7.433928 l
1707 | 213.863464 9.223700 l
1708 | 219.507629 9.223700 l
1709 | h
1710 | 215.256927 11.575973 m
1711 | 217.570847 11.575973 l
1712 | 217.570847 2.358643 l
1713 | 217.570847 2.047563 217.617722 1.808927 217.711472 1.642734 c
1714 | 217.809479 1.480802 217.937317 1.370007 218.094986 1.310347 c
1715 | 218.252655 1.250687 218.427383 1.220858 218.619141 1.220858 c
1716 | 218.764023 1.220858 218.896133 1.231512 219.015442 1.252819 c
1717 | 219.139023 1.274126 219.232773 1.293301 219.296692 1.310347 c
1718 | 219.686615 -0.498602 l
1719 | 219.563034 -0.541216 219.386185 -0.588091 219.156067 -0.639227 c
1720 | 218.930222 -0.690363 218.653229 -0.720192 218.325104 -0.728716 c
1721 | 217.745560 -0.745762 217.223541 -0.658403 216.759048 -0.466642 c
1722 | 216.294556 -0.270618 215.925949 0.031939 215.653229 0.441029 c
1723 | 215.384766 0.850119 215.252670 1.361483 215.256927 1.975120 c
1724 | 215.256927 11.575973 l
1725 | h
1726 | f
1727 | n
1728 | Q
1729 | q
1730 | 1.000000 0.000000 -0.000000 1.000000 54.000000 543.594482 cm
1731 | BT
1732 | 18.000000 0.000000 0.000000 18.000000 0.000000 -0.594482 Tm
1733 | /F1 1.000000 Tf
1734 | [ (\020) 7.990042 (\016) (\017) (\016) (\014) (\015) (\010) (\t) (\013) 20.685408 (\007) (\n) (\t) (\006) 102.006276 (\007) (\005) (\n) (\t) (\004) 9.676615 (\003) 19.886441 (\002) (\001) (\016) (\000) (\010) ] TJ
1735 | ET
1736 | Q
1737 | q
1738 | 1.000000 0.000000 -0.000000 1.000000 54.000000 513.477234 cm
1739 | 0.683333 0.683333 0.683333 scn
1740 | 0.000000 -0.477234 m
1741 | h
1742 | 0.676136 8.610834 m
1743 | 0.676136 9.704584 l
1744 | 8.312500 9.704584 l
1745 | 8.312500 8.610834 l
1746 | 5.110796 8.610834 l
1747 | 5.110796 -0.477234 l
1748 | 3.877841 -0.477234 l
1749 | 3.877841 8.610834 l
1750 | 0.676136 8.610834 l
1751 | h
1752 | 8.982422 -0.477234 m
1753 | h
1754 | 11.229581 4.116516 m
1755 | 11.229581 -0.477234 l
1756 | 10.056286 -0.477234 l
1757 | 10.056286 9.704584 l
1758 | 11.229581 9.704584 l
1759 | 11.229581 5.965948 l
1760 | 11.329013 5.965948 l
1761 | 11.507990 6.360361 11.776456 6.673571 12.134411 6.905579 c
1762 | 12.495680 7.140901 12.976267 7.258562 13.576172 7.258562 c
1763 | 14.096532 7.258562 14.552260 7.154158 14.943359 6.945352 c
1764 | 15.334458 6.739859 15.637725 6.423334 15.853161 5.995778 c
1765 | 16.071911 5.571535 16.181286 5.031289 16.181286 4.375039 c
1766 | 16.181286 -0.477234 l
1767 | 15.007990 -0.477234 l
1768 | 15.007990 4.295493 l
1769 | 15.007990 4.902027 14.850556 5.371015 14.535689 5.702454 c
1770 | 14.224136 6.037208 13.791608 6.204585 13.238104 6.204585 c
1771 | 12.853634 6.204585 12.508937 6.123382 12.204013 5.960977 c
1772 | 11.902403 5.798571 11.663767 5.561592 11.488104 5.250039 c
1773 | 11.315755 4.938486 11.229581 4.560645 11.229581 4.116516 c
1774 | h
1775 | 17.253906 -0.477234 m
1776 | h
1777 | 20.574928 -0.656211 m
1778 | 20.091028 -0.656211 19.651871 -0.565065 19.257458 -0.382773 c
1779 | 18.863045 -0.197167 18.549835 0.069641 18.317827 0.417652 c
1780 | 18.085819 0.768978 17.969816 1.193221 17.969816 1.690380 c
1781 | 17.969816 2.127880 18.055990 2.482520 18.228338 2.754300 c
1782 | 18.400686 3.029395 18.631037 3.244831 18.919390 3.400607 c
1783 | 19.207741 3.556383 19.525923 3.672387 19.873934 3.748618 c
1784 | 20.225260 3.828163 20.578243 3.891137 20.932884 3.937539 c
1785 | 21.396898 3.997198 21.773083 4.041943 22.061436 4.071772 c
1786 | 22.353102 4.104916 22.565224 4.159603 22.697798 4.235834 c
1787 | 22.833689 4.312065 22.901634 4.444641 22.901634 4.633562 c
1788 | 22.901634 4.673334 l
1789 | 22.901634 5.163865 22.767401 5.545020 22.498936 5.816800 c
1790 | 22.233784 6.088581 21.831085 6.224471 21.290838 6.224471 c
1791 | 20.730705 6.224471 20.291548 6.101838 19.973366 5.856573 c
1792 | 19.655184 5.611308 19.431463 5.349471 19.302202 5.071062 c
1793 | 18.188566 5.468789 l
1794 | 18.387428 5.932805 18.652580 6.294073 18.984020 6.552596 c
1795 | 19.318773 6.814433 19.683357 6.996725 20.077770 7.099471 c
1796 | 20.475496 7.205531 20.866596 7.258562 21.251066 7.258562 c
1797 | 21.496330 7.258562 21.778053 7.228732 22.096235 7.169074 c
1798 | 22.417732 7.112729 22.727627 6.995068 23.025923 6.816091 c
1799 | 23.327534 6.637113 23.577770 6.366990 23.776634 6.005721 c
1800 | 23.975498 5.644452 24.074930 5.160550 24.074930 4.554016 c
1801 | 24.074930 -0.477234 l
1802 | 22.901634 -0.477234 l
1803 | 22.901634 0.556857 l
1804 | 22.841974 0.556857 l
1805 | 22.762428 0.391137 22.629854 0.213817 22.444248 0.024897 c
1806 | 22.258642 -0.164023 22.011719 -0.324772 21.703480 -0.457348 c
1807 | 21.395243 -0.589923 21.019058 -0.656211 20.574928 -0.656211 c
1808 | h
1809 | 20.753906 0.397766 m
1810 | 21.217920 0.397766 21.609020 0.488912 21.927202 0.671204 c
1811 | 22.248699 0.853496 22.490648 1.088818 22.653053 1.377170 c
1812 | 22.818773 1.665522 22.901634 1.968789 22.901634 2.286971 c
1813 | 22.901634 3.360834 l
1814 | 22.851919 3.301175 22.742544 3.246488 22.573509 3.196772 c
1815 | 22.407789 3.150371 22.215553 3.108940 21.996803 3.072482 c
1816 | 21.781368 3.039338 21.570904 3.009508 21.365412 2.982993 c
1817 | 21.163235 2.959793 20.999172 2.939906 20.873224 2.923334 c
1818 | 20.568300 2.883561 20.283262 2.818931 20.018110 2.729443 c
1819 | 19.756273 2.643268 19.544151 2.512349 19.381746 2.336687 c
1820 | 19.222656 2.164338 19.143110 1.929016 19.143110 1.630721 c
1821 | 19.143110 1.223050 19.293915 0.914811 19.595526 0.706005 c
1822 | 19.900450 0.500512 20.286577 0.397766 20.753906 0.397766 c
1823 | h
1824 | 25.142578 -0.477234 m
1825 | h
1826 | 27.389738 4.116516 m
1827 | 27.389738 -0.477234 l
1828 | 26.216442 -0.477234 l
1829 | 26.216442 7.159130 l
1830 | 27.349964 7.159130 l
1831 | 27.349964 5.965948 l
1832 | 27.449396 5.965948 l
1833 | 27.628374 6.353732 27.900154 6.665285 28.264738 6.900607 c
1834 | 28.629322 7.139244 29.099966 7.258562 29.676670 7.258562 c
1835 | 30.193716 7.258562 30.646130 7.152501 31.033915 6.940380 c
1836 | 31.421698 6.731573 31.723307 6.413391 31.938744 5.985835 c
1837 | 32.154179 5.561592 32.261898 5.024660 32.261898 4.375039 c
1838 | 32.261898 -0.477234 l
1839 | 31.088602 -0.477234 l
1840 | 31.088602 4.295493 l
1841 | 31.088602 4.895399 30.932825 5.362728 30.621271 5.697482 c
1842 | 30.309719 6.035551 29.882162 6.204585 29.338600 6.204585 c
1843 | 28.964075 6.204585 28.629320 6.123382 28.334339 5.960977 c
1844 | 28.042673 5.798571 27.812323 5.561592 27.643288 5.250039 c
1845 | 27.474255 4.938486 27.389738 4.560645 27.389738 4.116516 c
1846 | h
1847 | 33.332031 -0.477234 m
1848 | h
1849 | 35.499645 2.306857 m
1850 | 35.479759 3.758561 l
1851 | 35.718395 3.758561 l
1852 | 39.059303 7.159130 l
1853 | 40.511009 7.159130 l
1854 | 36.951351 3.559698 l
1855 | 36.851917 3.559698 l
1856 | 35.499645 2.306857 l
1857 | h
1858 | 34.405895 -0.477234 m
1859 | 34.405895 9.704584 l
1860 | 35.579189 9.704584 l
1861 | 35.579189 -0.477234 l
1862 | 34.405895 -0.477234 l
1863 | h
1864 | 39.258167 -0.477234 m
1865 | 36.275211 3.301175 l
1866 | 37.110439 4.116516 l
1867 | 40.749645 -0.477234 l
1868 | 39.258167 -0.477234 l
1869 | h
1870 | 40.947266 -0.477234 m
1871 | h
1872 | 47.430222 5.448903 m
1873 | 46.376244 5.150607 l
1874 | 46.309956 5.326270 46.212181 5.496962 46.082920 5.662681 c
1875 | 45.956970 5.831715 45.784622 5.970920 45.565872 6.080295 c
1876 | 45.347122 6.189670 45.067055 6.244357 44.725674 6.244357 c
1877 | 44.258347 6.244357 43.868904 6.136639 43.557350 5.921204 c
1878 | 43.249111 5.709083 43.094994 5.438959 43.094994 5.110834 c
1879 | 43.094994 4.819167 43.201054 4.588818 43.413174 4.419784 c
1880 | 43.625298 4.250750 43.956738 4.109887 44.407494 3.997198 c
1881 | 45.541016 3.718789 l
1882 | 46.223782 3.553069 46.732540 3.299518 47.067295 2.958136 c
1883 | 47.402046 2.620068 47.569424 2.184225 47.569424 1.650607 c
1884 | 47.569424 1.213107 47.443478 0.822008 47.191586 0.477311 c
1885 | 46.943005 0.132614 46.594994 -0.139166 46.147549 -0.338029 c
1886 | 45.700104 -0.536893 45.179745 -0.636325 44.586472 -0.636325 c
1887 | 43.807590 -0.636325 43.162941 -0.467291 42.652523 -0.129223 c
1888 | 42.142105 0.208846 41.818951 0.702691 41.683060 1.352312 c
1889 | 42.796696 1.630721 l
1890 | 42.902756 1.219736 43.103279 0.911497 43.398258 0.706005 c
1891 | 43.696556 0.500512 44.085999 0.397766 44.566586 0.397766 c
1892 | 45.113461 0.397766 45.547646 0.513770 45.869141 0.745777 c
1893 | 46.193951 0.981099 46.356358 1.262823 46.356358 1.590948 c
1894 | 46.356358 1.856099 46.263554 2.078164 46.077946 2.257141 c
1895 | 45.892342 2.439433 45.607304 2.575323 45.222836 2.664812 c
1896 | 43.950108 2.963107 l
1897 | 43.250771 3.128826 42.737038 3.385692 42.408913 3.733704 c
1898 | 42.084103 4.085030 41.921696 4.524187 41.921696 5.051175 c
1899 | 41.921696 5.482046 42.042671 5.863202 42.284622 6.194641 c
1900 | 42.529888 6.526081 42.862988 6.786261 43.283913 6.975182 c
1901 | 43.708157 7.164102 44.188744 7.258562 44.725674 7.258562 c
1902 | 45.481354 7.258562 46.074631 7.092842 46.505505 6.761403 c
1903 | 46.939690 6.429964 47.247929 5.992464 47.430222 5.448903 c
1904 | h
1905 | 52.199219 -0.477234 m
1906 | h
1907 | 56.673649 7.159130 m
1908 | 56.673649 6.164812 l
1909 | 52.557175 6.164812 l
1910 | 52.557175 7.159130 l
1911 | 56.673649 7.159130 l
1912 | h
1913 | 53.790127 -0.477234 m
1914 | 53.790127 8.213107 l
1915 | 53.790127 8.650608 53.892872 9.015191 54.098366 9.306858 c
1916 | 54.303860 9.598524 54.570667 9.817274 54.898792 9.963108 c
1917 | 55.226917 10.108941 55.573273 10.181858 55.937855 10.181858 c
1918 | 56.226208 10.181858 56.461529 10.158657 56.643822 10.112255 c
1919 | 56.826115 10.065854 56.962002 10.022766 57.051491 9.982994 c
1920 | 56.713425 8.968789 l
1921 | 56.653767 8.988676 56.570904 9.013534 56.464844 9.043363 c
1922 | 56.362099 9.073193 56.226208 9.088108 56.057175 9.088108 c
1923 | 55.669392 9.088108 55.389324 8.990333 55.216976 8.794783 c
1924 | 55.047943 8.599234 54.963425 8.312539 54.963425 7.934699 c
1925 | 54.963425 -0.477234 l
1926 | 53.790127 -0.477234 l
1927 | h
1928 | 56.929688 -0.477234 m
1929 | h
1930 | 61.105824 -0.636325 m
1931 | 60.416428 -0.636325 59.811554 -0.472262 59.291195 -0.144137 c
1932 | 58.774147 0.183988 58.369793 0.643031 58.078125 1.232993 c
1933 | 57.789772 1.822956 57.645596 2.512350 57.645596 3.301175 c
1934 | 57.645596 4.096630 57.789772 4.790996 58.078125 5.384272 c
1935 | 58.369793 5.977549 58.774147 6.438250 59.291195 6.766374 c
1936 | 59.811554 7.094499 60.416428 7.258562 61.105824 7.258562 c
1937 | 61.795219 7.258562 62.398438 7.094499 62.915482 6.766374 c
1938 | 63.435841 6.438250 63.840199 5.977549 64.128548 5.384272 c
1939 | 64.420212 4.790996 64.566048 4.096630 64.566048 3.301175 c
1940 | 64.566048 2.512350 64.420212 1.822956 64.128548 1.232993 c
1941 | 63.840199 0.643031 63.435841 0.183988 62.915482 -0.144137 c
1942 | 62.398438 -0.472262 61.795219 -0.636325 61.105824 -0.636325 c
1943 | h
1944 | 61.105824 0.417652 m
1945 | 61.629498 0.417652 62.060368 0.551886 62.398438 0.820352 c
1946 | 62.736507 1.088818 62.986744 1.441800 63.149147 1.879300 c
1947 | 63.311554 2.316800 63.392757 2.790758 63.392757 3.301175 c
1948 | 63.392757 3.811592 63.311554 4.287208 63.149147 4.728022 c
1949 | 62.986744 5.168836 62.736507 5.525134 62.398438 5.796914 c
1950 | 62.060368 6.068695 61.629498 6.204585 61.105824 6.204585 c
1951 | 60.582150 6.204585 60.151279 6.068695 59.813210 5.796914 c
1952 | 59.475140 5.525134 59.224903 5.168836 59.062500 4.728022 c
1953 | 58.900097 4.287208 58.818893 3.811592 58.818893 3.301175 c
1954 | 58.818893 2.790758 58.900097 2.316800 59.062500 1.879300 c
1955 | 59.224903 1.441800 59.475140 1.088818 59.813210 0.820352 c
1956 | 60.151279 0.551886 60.582150 0.417652 61.105824 0.417652 c
1957 | h
1958 | 65.283203 -0.477234 m
1959 | h
1960 | 66.357063 -0.477234 m
1961 | 66.357063 7.159130 l
1962 | 67.490593 7.159130 l
1963 | 67.490593 6.005721 l
1964 | 67.570137 6.005721 l
1965 | 67.709335 6.383562 67.961227 6.690143 68.325813 6.925466 c
1966 | 68.690399 7.160788 69.101379 7.278449 69.558769 7.278449 c
1967 | 69.644943 7.278449 69.752663 7.276791 69.881927 7.273477 c
1968 | 70.011185 7.270162 70.108955 7.265191 70.175247 7.258562 c
1969 | 70.175247 6.065380 l
1970 | 70.135475 6.075323 70.044334 6.090238 69.901810 6.110125 c
1971 | 69.762604 6.133325 69.615112 6.144925 69.459343 6.144925 c
1972 | 69.088127 6.144925 68.756683 6.067037 68.465019 5.911261 c
1973 | 68.176666 5.758799 67.947975 5.546677 67.778938 5.274897 c
1974 | 67.613220 5.006431 67.530365 4.699850 67.530365 4.355153 c
1975 | 67.530365 -0.477234 l
1976 | 66.357063 -0.477234 l
1977 | h
1978 | 74.429688 -0.477234 m
1979 | h
1980 | 78.824577 7.159130 m
1981 | 78.824577 6.164812 l
1982 | 74.867188 6.164812 l
1983 | 74.867188 7.159130 l
1984 | 78.824577 7.159130 l
1985 | h
1986 | 76.020599 8.988675 m
1987 | 77.193893 8.988675 l
1988 | 77.193893 1.710266 l
1989 | 77.193893 1.378827 77.241951 1.130247 77.338066 0.964527 c
1990 | 77.437500 0.802122 77.563446 0.692747 77.715912 0.636402 c
1991 | 77.871689 0.583372 78.035751 0.556857 78.208099 0.556857 c
1992 | 78.337357 0.556857 78.443413 0.563486 78.526276 0.576743 c
1993 | 78.609138 0.593315 78.675423 0.606573 78.725143 0.616516 c
1994 | 78.963776 -0.437461 l
1995 | 78.884232 -0.467291 78.773201 -0.497120 78.630684 -0.526950 c
1996 | 78.488159 -0.560094 78.307526 -0.576666 78.088776 -0.576666 c
1997 | 77.757339 -0.576666 77.432533 -0.505406 77.114349 -0.362887 c
1998 | 76.799477 -0.220368 76.537643 -0.003276 76.328835 0.288391 c
1999 | 76.123344 0.580058 76.020599 0.947955 76.020599 1.392084 c
2000 | 76.020599 8.988675 l
2001 | h
2002 | 79.679688 -0.477234 m
2003 | h
2004 | 81.926849 4.116516 m
2005 | 81.926849 -0.477234 l
2006 | 80.753548 -0.477234 l
2007 | 80.753548 9.704584 l
2008 | 81.926849 9.704584 l
2009 | 81.926849 5.965948 l
2010 | 82.026276 5.965948 l
2011 | 82.205254 6.360361 82.473724 6.673571 82.831673 6.905579 c
2012 | 83.192947 7.140901 83.673531 7.258562 84.273438 7.258562 c
2013 | 84.793800 7.258562 85.249527 7.154158 85.640625 6.945352 c
2014 | 86.031723 6.739859 86.334991 6.423334 86.550423 5.995778 c
2015 | 86.769173 5.571535 86.878548 5.031289 86.878548 4.375039 c
2016 | 86.878548 -0.477234 l
2017 | 85.705254 -0.477234 l
2018 | 85.705254 4.295493 l
2019 | 85.705254 4.902027 85.547821 5.371015 85.232956 5.702454 c
2020 | 84.921402 6.037208 84.488876 6.204585 83.935371 6.204585 c
2021 | 83.550903 6.204585 83.206200 6.123382 82.901276 5.960977 c
2022 | 82.599663 5.798571 82.361031 5.561592 82.185371 5.250039 c
2023 | 82.013023 4.938486 81.926849 4.560645 81.926849 4.116516 c
2024 | h
2025 | 87.951172 -0.477234 m
2026 | h
2027 | 92.226738 -0.636325 m
2028 | 91.490944 -0.636325 90.856239 -0.473920 90.322624 -0.149109 c
2029 | 89.792320 0.179016 89.382988 0.636402 89.094635 1.223050 c
2030 | 88.809601 1.813012 88.667084 2.499092 88.667084 3.281289 c
2031 | 88.667084 4.063486 88.809601 4.752880 89.094635 5.349471 c
2032 | 89.382988 5.949376 89.784035 6.416706 90.297760 6.751460 c
2033 | 90.814812 7.089528 91.418030 7.258562 92.107422 7.258562 c
2034 | 92.505150 7.258562 92.897903 7.192274 93.285690 7.059699 c
2035 | 93.673470 6.927123 94.026451 6.711687 94.344635 6.413391 c
2036 | 94.662819 6.118410 94.916367 5.727312 95.105293 5.240096 c
2037 | 95.294212 4.752880 95.388672 4.152974 95.388672 3.440380 c
2038 | 95.388672 2.943221 l
2039 | 89.502312 2.943221 l
2040 | 89.502312 3.957425 l
2041 | 94.195488 3.957425 l
2042 | 94.195488 4.388296 94.109314 4.772766 93.936966 5.110834 c
2043 | 93.767937 5.448903 93.525986 5.715712 93.211113 5.911261 c
2044 | 92.899559 6.106810 92.531662 6.204585 92.107422 6.204585 c
2045 | 91.640091 6.204585 91.235733 6.088581 90.894356 5.856573 c
2046 | 90.556290 5.627880 90.296104 5.329585 90.113815 4.961687 c
2047 | 89.931526 4.593789 89.840378 4.199376 89.840378 3.778448 c
2048 | 89.840378 3.102311 l
2049 | 89.840378 2.525607 89.939812 2.036735 90.138672 1.635693 c
2050 | 90.340851 1.237966 90.620918 0.934698 90.978874 0.725891 c
2051 | 91.336823 0.520398 91.752777 0.417652 92.226738 0.417652 c
2052 | 92.534973 0.417652 92.813385 0.460739 93.061966 0.546914 c
2053 | 93.313866 0.636402 93.530960 0.768978 93.713249 0.944641 c
2054 | 93.895538 1.123618 94.036400 1.345683 94.135834 1.610834 c
2055 | 95.269356 1.292652 l
2056 | 95.150040 0.908183 94.949516 0.570115 94.667793 0.278448 c
2057 | 94.386070 -0.009904 94.038055 -0.235283 93.623756 -0.397689 c
2058 | 93.209457 -0.556780 92.743790 -0.636325 92.226738 -0.636325 c
2059 | h
2060 | 100.037109 -0.477234 m
2061 | h
2062 | 101.110970 -3.340870 m
2063 | 101.110970 7.159130 l
2064 | 102.244499 7.159130 l
2065 | 102.244499 5.946062 l
2066 | 102.383698 5.946062 l
2067 | 102.469872 6.078638 102.589195 6.247672 102.741653 6.453164 c
2068 | 102.897430 6.661971 103.119492 6.847577 103.407845 7.009982 c
2069 | 103.699509 7.175702 104.093925 7.258562 104.591087 7.258562 c
2070 | 105.234077 7.258562 105.800842 7.097814 106.291374 6.776318 c
2071 | 106.781906 6.454821 107.164719 5.999092 107.439812 5.409130 c
2072 | 107.714905 4.819168 107.852448 4.123145 107.852448 3.321061 c
2073 | 107.852448 2.512349 107.714905 1.811355 107.439812 1.218079 c
2074 | 107.164719 0.628117 106.783562 0.170731 106.296341 -0.154080 c
2075 | 105.809128 -0.475576 105.247337 -0.636325 104.610970 -0.636325 c
2076 | 104.120438 -0.636325 103.727684 -0.555122 103.432709 -0.392716 c
2077 | 103.137726 -0.226997 102.910690 -0.039734 102.751595 0.169073 c
2078 | 102.592506 0.381194 102.469872 0.556857 102.383698 0.696061 c
2079 | 102.284271 0.696061 l
2080 | 102.284271 -3.340870 l
2081 | 101.110970 -3.340870 l
2082 | h
2083 | 102.264381 3.340948 m
2084 | 102.264381 2.764243 102.348900 2.255484 102.517937 1.814670 c
2085 | 102.686966 1.377170 102.933891 1.034130 103.258698 0.785550 c
2086 | 103.583511 0.540285 103.981239 0.417652 104.451881 0.417652 c
2087 | 104.942413 0.417652 105.351738 0.546913 105.679863 0.805436 c
2088 | 106.011307 1.067274 106.259888 1.418600 106.425606 1.859414 c
2089 | 106.594635 2.303543 106.679153 2.797387 106.679153 3.340948 c
2090 | 106.679153 3.877880 106.596291 4.361782 106.430573 4.792653 c
2091 | 106.268166 5.226838 106.021248 5.569878 105.689812 5.821772 c
2092 | 105.361687 6.076981 104.949043 6.204585 104.451881 6.204585 c
2093 | 103.974609 6.204585 103.573563 6.083609 103.248756 5.841658 c
2094 | 102.923950 5.603022 102.678680 5.268268 102.512962 4.837397 c
2095 | 102.347244 4.409841 102.264381 3.911024 102.264381 3.340948 c
2096 | h
2097 | 108.568359 -0.477234 m
2098 | h
2099 | 111.889381 -0.656211 m
2100 | 111.405479 -0.656211 110.966324 -0.565065 110.571907 -0.382773 c
2101 | 110.177498 -0.197167 109.864288 0.069641 109.632278 0.417652 c
2102 | 109.400276 0.768978 109.284271 1.193221 109.284271 1.690380 c
2103 | 109.284271 2.127880 109.370445 2.482520 109.542793 2.754300 c
2104 | 109.715141 3.029395 109.945488 3.244831 110.233841 3.400607 c
2105 | 110.522194 3.556383 110.840378 3.672387 111.188385 3.748618 c
2106 | 111.539711 3.828163 111.892700 3.891137 112.247337 3.937539 c
2107 | 112.711349 3.997198 113.087532 4.041943 113.375885 4.071772 c
2108 | 113.667549 4.104916 113.879677 4.159603 114.012253 4.235834 c
2109 | 114.148140 4.312065 114.216087 4.444641 114.216087 4.633562 c
2110 | 114.216087 4.673334 l
2111 | 114.216087 5.163865 114.081856 5.545020 113.813385 5.816800 c
2112 | 113.548233 6.088581 113.145538 6.224471 112.605293 6.224471 c
2113 | 112.045158 6.224471 111.606003 6.101838 111.287819 5.856573 c
2114 | 110.969635 5.611308 110.745918 5.349471 110.616653 5.071062 c
2115 | 109.503021 5.468789 l
2116 | 109.701881 5.932805 109.967033 6.294073 110.298470 6.552596 c
2117 | 110.633224 6.814433 110.997810 6.996725 111.392220 7.099471 c
2118 | 111.789948 7.205531 112.181053 7.258562 112.565521 7.258562 c
2119 | 112.810783 7.258562 113.092506 7.228732 113.410690 7.169074 c
2120 | 113.732185 7.112729 114.042084 6.995068 114.340378 6.816091 c
2121 | 114.641991 6.637113 114.892227 6.366990 115.091087 6.005721 c
2122 | 115.289948 5.644452 115.389381 5.160550 115.389381 4.554016 c
2123 | 115.389381 -0.477234 l
2124 | 114.216087 -0.477234 l
2125 | 114.216087 0.556857 l
2126 | 114.156425 0.556857 l
2127 | 114.076881 0.391137 113.944305 0.213817 113.758698 0.024897 c
2128 | 113.573097 -0.164023 113.326172 -0.324772 113.017937 -0.457348 c
2129 | 112.709694 -0.589923 112.333511 -0.656211 111.889381 -0.656211 c
2130 | h
2131 | 112.068359 0.397766 m
2132 | 112.532372 0.397766 112.923470 0.488912 113.241653 0.671204 c
2133 | 113.563148 0.853496 113.805099 1.088818 113.967506 1.377170 c
2134 | 114.133224 1.665522 114.216087 1.968789 114.216087 2.286971 c
2135 | 114.216087 3.360834 l
2136 | 114.166367 3.301175 114.056992 3.246488 113.887962 3.196772 c
2137 | 113.722244 3.150371 113.530006 3.108940 113.311256 3.072482 c
2138 | 113.095825 3.039338 112.885361 3.009508 112.679863 2.982993 c
2139 | 112.477684 2.959793 112.313622 2.939906 112.187675 2.923334 c
2140 | 111.882751 2.883561 111.597717 2.818931 111.332565 2.729443 c
2141 | 111.070732 2.643268 110.858604 2.512349 110.696198 2.336687 c
2142 | 110.537109 2.164338 110.457565 1.929016 110.457565 1.630721 c
2143 | 110.457565 1.223050 110.608368 0.914811 110.909981 0.706005 c
2144 | 111.214905 0.500512 111.601028 0.397766 112.068359 0.397766 c
2145 | h
2146 | 116.183594 -0.477234 m
2147 | h
2148 | 118.013138 -3.340870 m
2149 | 117.814278 -3.340870 117.636955 -3.324299 117.481178 -3.291155 c
2150 | 117.325401 -3.261326 117.217682 -3.231495 117.158028 -3.201666 c
2151 | 117.456322 -2.167575 l
2152 | 117.741364 -2.240492 117.993256 -2.267007 118.212006 -2.247120 c
2153 | 118.430756 -2.227234 118.624649 -2.129459 118.793678 -1.953796 c
2154 | 118.966026 -1.781447 119.123459 -1.501382 119.265984 -1.113598 c
2155 | 119.484734 -0.517007 l
2156 | 116.660866 7.159130 l
2157 | 117.933594 7.159130 l
2158 | 120.041550 1.073902 l
2159 | 120.121094 1.073902 l
2160 | 122.229050 7.159130 l
2161 | 123.501778 7.159130 l
2162 | 120.260300 -1.590870 l
2163 | 120.114464 -1.985283 119.933830 -2.311751 119.718391 -2.570274 c
2164 | 119.502960 -2.832111 119.252724 -3.026003 118.967682 -3.151949 c
2165 | 118.685959 -3.277897 118.367775 -3.340870 118.013138 -3.340870 c
2166 | h
2167 | 123.976562 -0.477234 m
2168 | h
2169 | 125.050423 -0.477234 m
2170 | 125.050423 7.159130 l
2171 | 126.183952 7.159130 l
2172 | 126.183952 5.965948 l
2173 | 126.283379 5.965948 l
2174 | 126.442474 6.373619 126.699341 6.690143 127.053978 6.915522 c
2175 | 127.408615 7.144215 127.834518 7.258562 128.331680 7.258562 c
2176 | 128.835464 7.258562 129.254730 7.144215 129.589493 6.915522 c
2177 | 129.927551 6.690143 130.191055 6.373619 130.379974 5.965948 c
2178 | 130.459518 5.965948 l
2179 | 130.655060 6.360361 130.948395 6.673571 131.339493 6.905579 c
2180 | 131.730591 7.140901 132.199570 7.258562 132.746445 7.258562 c
2181 | 133.429214 7.258562 133.987686 7.044783 134.421875 6.617227 c
2182 | 134.856064 6.192984 135.073151 5.531763 135.073151 4.633562 c
2183 | 135.073151 -0.477234 l
2184 | 133.899857 -0.477234 l
2185 | 133.899857 4.633562 l
2186 | 133.899857 5.197009 133.745743 5.599708 133.437500 5.841658 c
2187 | 133.129257 6.083609 132.766342 6.204585 132.348724 6.204585 c
2188 | 131.811783 6.204585 131.395828 6.042179 131.100845 5.717369 c
2189 | 130.805878 5.395872 130.658386 4.988202 130.658386 4.494357 c
2190 | 130.658386 -0.477234 l
2191 | 129.465195 -0.477234 l
2192 | 129.465195 4.752880 l
2193 | 129.465195 5.187066 129.324341 5.536734 129.042618 5.801886 c
2194 | 128.760895 6.070352 128.397964 6.204585 127.953835 6.204585 c
2195 | 127.648911 6.204585 127.363876 6.123382 127.098724 5.960977 c
2196 | 126.836884 5.798571 126.624763 5.573193 126.462357 5.284841 c
2197 | 126.303268 4.999803 126.223724 4.670020 126.223724 4.295493 c
2198 | 126.223724 -0.477234 l
2199 | 125.050423 -0.477234 l
2200 | h
2201 | 136.144531 -0.477234 m
2202 | h
2203 | 140.420105 -0.636325 m
2204 | 139.684311 -0.636325 139.049591 -0.473920 138.515976 -0.149109 c
2205 | 137.985672 0.179016 137.576340 0.636402 137.287994 1.223050 c
2206 | 137.002960 1.813012 136.860443 2.499092 136.860443 3.281289 c
2207 | 136.860443 4.063486 137.002960 4.752880 137.287994 5.349471 c
2208 | 137.576340 5.949376 137.977386 6.416706 138.491119 6.751460 c
2209 | 139.008163 7.089528 139.611389 7.258562 140.300781 7.258562 c
2210 | 140.698502 7.258562 141.091263 7.192274 141.479050 7.059699 c
2211 | 141.866837 6.927123 142.219818 6.711687 142.537994 6.413391 c
2212 | 142.856186 6.118410 143.109726 5.727312 143.298645 5.240096 c
2213 | 143.487564 4.752880 143.582031 4.152974 143.582031 3.440380 c
2214 | 143.582031 2.943221 l
2215 | 137.695663 2.943221 l
2216 | 137.695663 3.957425 l
2217 | 142.388855 3.957425 l
2218 | 142.388855 4.388296 142.302673 4.772766 142.130325 5.110834 c
2219 | 141.961288 5.448903 141.719345 5.715712 141.404480 5.911261 c
2220 | 141.092926 6.106810 140.725021 6.204585 140.300781 6.204585 c
2221 | 139.833450 6.204585 139.429092 6.088581 139.087708 5.856573 c
2222 | 138.749649 5.627880 138.489471 5.329585 138.307175 4.961687 c
2223 | 138.124878 4.593789 138.033737 4.199376 138.033737 3.778448 c
2224 | 138.033737 3.102311 l
2225 | 138.033737 2.525607 138.133163 2.036735 138.332031 1.635693 c
2226 | 138.534210 1.237966 138.814270 0.934698 139.172226 0.725891 c
2227 | 139.530182 0.520398 139.946152 0.417652 140.420105 0.417652 c
2228 | 140.728348 0.417652 141.006744 0.460739 141.255325 0.546914 c
2229 | 141.507217 0.636402 141.724304 0.768978 141.906601 0.944641 c
2230 | 142.088898 1.123618 142.229752 1.345683 142.329193 1.610834 c
2231 | 143.462708 1.292652 l
2232 | 143.343399 0.908183 143.142868 0.570115 142.861145 0.278448 c
2233 | 142.579422 -0.009904 142.231415 -0.235283 141.817123 -0.397689 c
2234 | 141.402817 -0.556780 140.937149 -0.636325 140.420105 -0.636325 c
2235 | h
2236 | 144.292969 -0.477234 m
2237 | h
2238 | 146.540131 4.116516 m
2239 | 146.540131 -0.477234 l
2240 | 145.366837 -0.477234 l
2241 | 145.366837 7.159130 l
2242 | 146.500351 7.159130 l
2243 | 146.500351 5.965948 l
2244 | 146.599792 5.965948 l
2245 | 146.778763 6.353732 147.050552 6.665285 147.415131 6.900607 c
2246 | 147.779709 7.139244 148.250351 7.258562 148.827057 7.258562 c
2247 | 149.344101 7.258562 149.796524 7.152501 150.184311 6.940380 c
2248 | 150.572083 6.731573 150.873688 6.413391 151.089127 5.985835 c
2249 | 151.304565 5.561592 151.412292 5.024660 151.412292 4.375039 c
2250 | 151.412292 -0.477234 l
2251 | 150.238998 -0.477234 l
2252 | 150.238998 4.295493 l
2253 | 150.238998 4.895399 150.083221 5.362728 149.771667 5.697482 c
2254 | 149.460114 6.035551 149.032562 6.204585 148.488998 6.204585 c
2255 | 148.114471 6.204585 147.779709 6.123382 147.484726 5.960977 c
2256 | 147.193054 5.798571 146.962708 5.561592 146.793686 5.250039 c
2257 | 146.624649 4.938486 146.540131 4.560645 146.540131 4.116516 c
2258 | h
2259 | 152.482422 -0.477234 m
2260 | h
2261 | 156.877304 7.159130 m
2262 | 156.877304 6.164812 l
2263 | 152.919922 6.164812 l
2264 | 152.919922 7.159130 l
2265 | 156.877304 7.159130 l
2266 | h
2267 | 154.073334 8.988675 m
2268 | 155.246628 8.988675 l
2269 | 155.246628 1.710266 l
2270 | 155.246628 1.378827 155.294693 1.130247 155.390808 0.964527 c
2271 | 155.490234 0.802122 155.616180 0.692747 155.768646 0.636402 c
2272 | 155.924423 0.583372 156.088486 0.556857 156.260834 0.556857 c
2273 | 156.390091 0.556857 156.496155 0.563486 156.579010 0.576743 c
2274 | 156.661880 0.593315 156.728165 0.606573 156.777878 0.616516 c
2275 | 157.016510 -0.437461 l
2276 | 156.936966 -0.467291 156.825943 -0.497120 156.683411 -0.526950 c
2277 | 156.540894 -0.560094 156.360260 -0.576666 156.141510 -0.576666 c
2278 | 155.810074 -0.576666 155.485260 -0.505406 155.167084 -0.362887 c
2279 | 154.852219 -0.220368 154.590378 -0.003276 154.381577 0.288391 c
2280 | 154.176086 0.580058 154.073334 0.947955 154.073334 1.392084 c
2281 | 154.073334 8.988675 l
2282 | h
2283 | 157.568359 -0.477234 m
2284 | h
2285 | 159.497330 -0.556779 m
2286 | 159.252075 -0.556779 159.041611 -0.468947 158.865952 -0.293285 c
2287 | 158.690277 -0.117622 158.602448 0.092842 158.602448 0.338107 c
2288 | 158.602448 0.583372 158.690277 0.793837 158.865952 0.969499 c
2289 | 159.041611 1.145162 159.252075 1.232993 159.497330 1.232993 c
2290 | 159.742599 1.232993 159.953064 1.145162 160.128723 0.969499 c
2291 | 160.304398 0.793837 160.392227 0.583372 160.392227 0.338107 c
2292 | 160.392227 0.175701 160.350800 0.026554 160.267929 -0.109336 c
2293 | 160.188385 -0.245226 160.080673 -0.354601 159.944778 -0.437461 c
2294 | 159.812195 -0.517006 159.663055 -0.556779 159.497330 -0.556779 c
2295 | h
2296 | f
2297 | n
2298 | Q
2299 | q
2300 | 1.000000 0.000000 -0.000000 1.000000 54.000000 513.477234 cm
2301 | BT
2302 | 14.000000 0.000000 0.000000 14.000000 0.000000 -0.477234 Tm
2303 | /F1 1.000000 Tf
2304 | [ (\040) (\034) (\033) (\031) (\032) (\027) (\030) (\026) 22.904805 (\036) (\035) (\030) (\025) -11.363438 (\034) (\023) (\030) (\037) (\033) 19.975390 (\022) (\024) (\023) (\031) (\025) (\021) ] TJ
2305 | ET
2306 | Q
2307 | q
2308 | 1.000000 0.000000 -0.000000 1.000000 54.000000 54.000000 cm
2309 | 0.112500 0.112500 0.112500 scn
2310 | 0.000000 406.000000 m
2311 | 0.000000 409.313721 2.686292 412.000000 6.000000 412.000000 c
2312 | 386.000000 412.000000 l
2313 | 389.313721 412.000000 392.000000 409.313721 392.000000 406.000000 c
2314 | 392.000000 6.000000 l
2315 | 392.000000 2.686279 389.313721 0.000000 386.000000 0.000000 c
2316 | 5.999997 0.000000 l
2317 | 2.686289 0.000000 0.000000 2.686279 0.000000 6.000000 c
2318 | 0.000000 406.000000 l
2319 | h
2320 | f
2321 | n
2322 | Q
2323 |
2324 | endstream
2325 | endobj
2326 |
2327 | 89 0 obj
2328 | 59172
2329 | endobj
2330 |
2331 | 90 0 obj
2332 | << /Annots []
2333 | /Type /Page
2334 | /MediaBox [ 0.000000 0.000000 500.000000 700.000000 ]
2335 | /Resources 87 0 R
2336 | /Contents 88 0 R
2337 | /Parent 91 0 R
2338 | >>
2339 | endobj
2340 |
2341 | 91 0 obj
2342 | << /Kids [ 90 0 R ]
2343 | /Count 1
2344 | /Type /Pages
2345 | >>
2346 | endobj
2347 |
2348 | 92 0 obj
2349 | << /Pages 91 0 R
2350 | /Type /Catalog
2351 | >>
2352 | endobj
2353 |
2354 | xref
2355 | 0 93
2356 | 0000000000 65535 f
2357 | 0000000010 00000 n
2358 | 0000000117 00000 n
2359 | 0000000138 00000 n
2360 | 0000000245 00000 n
2361 | 0000000266 00000 n
2362 | 0000000372 00000 n
2363 | 0000000393 00000 n
2364 | 0000000500 00000 n
2365 | 0000000521 00000 n
2366 | 0000000628 00000 n
2367 | 0000000650 00000 n
2368 | 0000000759 00000 n
2369 | 0000000781 00000 n
2370 | 0000000889 00000 n
2371 | 0000000911 00000 n
2372 | 0000001019 00000 n
2373 | 0000001041 00000 n
2374 | 0000001149 00000 n
2375 | 0000001171 00000 n
2376 | 0000001280 00000 n
2377 | 0000001302 00000 n
2378 | 0000001411 00000 n
2379 | 0000001433 00000 n
2380 | 0000001541 00000 n
2381 | 0000001563 00000 n
2382 | 0000001672 00000 n
2383 | 0000001694 00000 n
2384 | 0000001802 00000 n
2385 | 0000001824 00000 n
2386 | 0000001933 00000 n
2387 | 0000001955 00000 n
2388 | 0000002063 00000 n
2389 | 0000002085 00000 n
2390 | 0000002194 00000 n
2391 | 0000002216 00000 n
2392 | 0000002324 00000 n
2393 | 0000002346 00000 n
2394 | 0000002454 00000 n
2395 | 0000002476 00000 n
2396 | 0000002584 00000 n
2397 | 0000002606 00000 n
2398 | 0000002714 00000 n
2399 | 0000002736 00000 n
2400 | 0000002845 00000 n
2401 | 0000002867 00000 n
2402 | 0000002976 00000 n
2403 | 0000002998 00000 n
2404 | 0000003106 00000 n
2405 | 0000003128 00000 n
2406 | 0000003237 00000 n
2407 | 0000003259 00000 n
2408 | 0000003368 00000 n
2409 | 0000003390 00000 n
2410 | 0000003499 00000 n
2411 | 0000003521 00000 n
2412 | 0000003629 00000 n
2413 | 0000003651 00000 n
2414 | 0000003760 00000 n
2415 | 0000003782 00000 n
2416 | 0000003891 00000 n
2417 | 0000003913 00000 n
2418 | 0000004021 00000 n
2419 | 0000004043 00000 n
2420 | 0000004151 00000 n
2421 | 0000004173 00000 n
2422 | 0000004281 00000 n
2423 | 0000004303 00000 n
2424 | 0000004623 00000 n
2425 | 0000006165 00000 n
2426 | 0000006189 00000 n
2427 | 0000007751 00000 n
2428 | 0000008050 00000 n
2429 | 0000008073 00000 n
2430 | 0000010230 00000 n
2431 | 0000010254 00000 n
2432 | 0000010553 00000 n
2433 | 0000010576 00000 n
2434 | 0000013851 00000 n
2435 | 0000013875 00000 n
2436 | 0000014397 00000 n
2437 | 0000014420 00000 n
2438 | 0000014942 00000 n
2439 | 0000014965 00000 n
2440 | 0000015687 00000 n
2441 | 0000015710 00000 n
2442 | 0000016432 00000 n
2443 | 0000016455 00000 n
2444 | 0000019462 00000 n
2445 | 0000078692 00000 n
2446 | 0000078717 00000 n
2447 | 0000078896 00000 n
2448 | 0000078972 00000 n
2449 | trailer
2450 | << /ID [ (some) (id) ]
2451 | /Root 92 0 R
2452 | /Size 93
2453 | >>
2454 | startxref
2455 | 79033
2456 | %%EOF
--------------------------------------------------------------------------------
/sig/resend.rbs:
--------------------------------------------------------------------------------
1 | module Resend
2 | VERSION: String
3 | # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4 | end
5 |
--------------------------------------------------------------------------------
/spec/api_keys_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | RSpec.describe "API Keys" do
4 | context 'static api_key' do
5 | before do
6 | Resend.configure do |config|
7 | config.api_key = "re_123"
8 | end
9 | end
10 |
11 | describe "create" do
12 |
13 | it "should create api key" do
14 | resp = {
15 | "id": "dacf4072-4119-4d88-932f-6202748ac7c8",
16 | "token": "re_c1tpEyD8_NKFusih9vKVQknRAQfmFcWCv"
17 | }
18 | params = {
19 | "name": "production"
20 | }
21 | allow_any_instance_of(Resend::Request).to receive(:perform).and_return(resp)
22 | expect(Resend::ApiKeys.create(params)[:id]).to eql("dacf4072-4119-4d88-932f-6202748ac7c8")
23 | end
24 |
25 | it "should raise when permission is invalid" do
26 | resp = {
27 | "statusCode" => 422,
28 | "name" => "invalid_permission",
29 | "message" => "Access must be 'full_access' | 'sending_access'"
30 | }
31 | allow(resp).to receive(:body).and_return(resp)
32 | params = {
33 | "name": "production",
34 | "permission": "invalid"
35 | }
36 | allow(HTTParty).to receive(:send).and_return(resp)
37 | expect { Resend::ApiKeys.create params }.to raise_error(Resend::Error::InvalidRequestError, /Access must be 'full_access' | 'sending_access'/)
38 | end
39 | end
40 |
41 | describe "list" do
42 | it "should list api keys" do
43 | resp = {
44 | "data": [
45 | {
46 | "id":"6e3c3d83-05dc-4b51-acfc-fe8972738bd0",
47 | "name":"test1",
48 | "created_at":"2023-04-21T01:31:02.671414+00:00"
49 | }
50 | ]
51 | }
52 | allow_any_instance_of(Resend::Request).to receive(:perform).and_return(resp)
53 | expect(Resend::ApiKeys.list.length).to eql(1)
54 | end
55 | end
56 |
57 | describe "remove" do
58 | it "should remove api key" do
59 | allow_any_instance_of(Resend::Request).to receive(:perform).and_return("")
60 | expect { Resend::ApiKeys.remove }.not_to raise_error
61 | end
62 | end
63 | end
64 |
65 | context 'dynamic api_key' do
66 | before do
67 | Resend.configure do |config|
68 | config.api_key = -> { "re_123" }
69 | end
70 | end
71 |
72 | describe "create" do
73 |
74 | it "should create api key" do
75 | resp = {
76 | "id": "dacf4072-4119-4d88-932f-6202748ac7c8",
77 | "token": "re_c1tpEyD8_NKFusih9vKVQknRAQfmFcWCv"
78 | }
79 | params = {
80 | "name": "production"
81 | }
82 | allow_any_instance_of(Resend::Request).to receive(:perform).and_return(resp)
83 | expect(Resend::ApiKeys.create(params)[:id]).to eql("dacf4072-4119-4d88-932f-6202748ac7c8")
84 | end
85 |
86 | it "should raise when permission is invalid" do
87 | resp = {
88 | "statusCode" => 422,
89 | "name" => "invalid_permission",
90 | "message" => "Access must be 'full_access' | 'sending_access'"
91 | }
92 | allow(resp).to receive(:body).and_return(resp)
93 | params = {
94 | "name": "production",
95 | "permission": "invalid"
96 | }
97 | allow(HTTParty).to receive(:send).and_return(resp)
98 | expect { Resend::ApiKeys.create params }.to raise_error(Resend::Error::InvalidRequestError, /Access must be 'full_access' | 'sending_access'/)
99 | end
100 | end
101 |
102 | describe "list" do
103 | it "should list api keys" do
104 | resp = {
105 | "data": [
106 | {
107 | "id":"6e3c3d83-05dc-4b51-acfc-fe8972738bd0",
108 | "name":"test1",
109 | "created_at":"2023-04-21T01:31:02.671414+00:00"
110 | }
111 | ]
112 | }
113 | allow_any_instance_of(Resend::Request).to receive(:perform).and_return(resp)
114 | expect(Resend::ApiKeys.list.length).to eql(1)
115 | end
116 | end
117 |
118 | describe "remove" do
119 | it "should remove api key" do
120 | allow_any_instance_of(Resend::Request).to receive(:perform).and_return("")
121 | expect { Resend::ApiKeys.remove }.not_to raise_error
122 | end
123 | end
124 | end
125 | end
126 |
--------------------------------------------------------------------------------
/spec/audiences_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | RSpec.describe "Audiences" do
4 |
5 | describe "create audience" do
6 |
7 | before do
8 | Resend.configure do |config|
9 | config.api_key = "re_123"
10 | end
11 | end
12 |
13 | it "should create an audience record" do
14 | resp = {
15 | "object": "audience",
16 | "id": "78261eea-8f8b-4381-83c6-79fa7120f1cf",
17 | "name": "Registered Users"
18 | }
19 | params = {
20 | "name": "Registered Users",
21 | }
22 | allow_any_instance_of(Resend::Request).to receive(:perform).and_return(resp)
23 | audience = Resend::Audiences.create(params)
24 | expect(audience[:id]).to eql("78261eea-8f8b-4381-83c6-79fa7120f1cf")
25 | expect(audience[:name]).to eql("Registered Users")
26 | expect(audience[:object]).to eql("audience")
27 | end
28 | end
29 |
30 | describe "get audience" do
31 | it "should retrieve audience" do
32 | resp = {
33 | "object": "audience",
34 | "id": "78261eea-8f8b-4381-83c6-79fa7120f1cf",
35 | "name": "Registered Users",
36 | "created_at": "2023-10-06T22:59:55.977Z"
37 | }
38 | allow(resp).to receive(:body).and_return(resp)
39 | allow(HTTParty).to receive(:send).and_return(resp)
40 |
41 | audience = Resend::Audiences.get(resp[:id])
42 |
43 | expect(audience[:object]).to eql "audience"
44 | expect(audience[:id]).to eql "78261eea-8f8b-4381-83c6-79fa7120f1cf"
45 | expect(audience[:name]).to eql "Registered Users"
46 | expect(audience[:created_at]).to eql "2023-10-06T22:59:55.977Z"
47 | end
48 | end
49 |
50 | describe "list audiences" do
51 | it "should list audiences" do
52 | resp = {
53 | "object": "list",
54 | "data": [
55 | {
56 | "id": "78261eea-8f8b-4381-83c6-79fa7120f1cf",
57 | "name": "Registered Users",
58 | "created_at": "2023-10-06T22:59:55.977Z"
59 | }
60 | ]
61 | }
62 | allow_any_instance_of(Resend::Request).to receive(:perform).and_return(resp)
63 | audiences = Resend::Audiences.list
64 | expect(audiences[:object]).to eql "list"
65 | expect(audiences[:data].empty?).to be false
66 | expect(audiences[:data].length).to eql(1)
67 | expect(audiences[:data].first[:id]).to eql("78261eea-8f8b-4381-83c6-79fa7120f1cf")
68 | expect(audiences[:data].first[:name]).to eql("Registered Users")
69 | end
70 | end
71 |
72 | describe "remove audience" do
73 | it "should remove audience by id" do
74 |
75 | resp = {
76 | "object": "audience",
77 | "id": "78261eea-8f8b-4381-83c6-79fa7120f1cf",
78 | "deleted": true
79 | }
80 |
81 | allow(resp).to receive(:body).and_return(resp)
82 | allow(HTTParty).to receive(:send).and_return(resp)
83 |
84 | deleted = Resend::Audiences.remove("78261eea-8f8b-4381-83c6-79fa7120f1cf")
85 | expect(deleted[:object]).to eql("audience")
86 | expect(deleted[:id]).to eql("78261eea-8f8b-4381-83c6-79fa7120f1cf")
87 | expect(deleted[:deleted]).to be true
88 | end
89 | end
90 | end
91 |
--------------------------------------------------------------------------------
/spec/batch_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | RSpec.describe "Batch" do
4 | describe "#send" do
5 |
6 | before do
7 | Resend.api_key = "re_123"
8 | end
9 |
10 | it "should send batch email" do
11 | resp = {
12 | "data": [
13 | {
14 | "id": "ae2014de-c168-4c61-8267-70d2662a1ce1"
15 | },
16 | {
17 | "id": "faccb7a5-8a28-4e9a-ac64-8da1cc3bc1cb"
18 | }
19 | ]
20 | }
21 |
22 | params = [
23 | {
24 | "from": "from@e.io",
25 | "to": ["email1@email.com"],
26 | "text": "testing",
27 | "subject": "Hey",
28 | },
29 | {
30 | "from": "from@e.io",
31 | "to": ["email2@email.com"],
32 | "text": "testing",
33 | "subject": "Hello",
34 | },
35 | ]
36 | allow_any_instance_of(Resend::Request).to receive(:perform).and_return(resp)
37 | emails = Resend::Batch.send(params)
38 | expect(emails[:data].length).to eq 2
39 | end
40 |
41 | it "does not send the Idempotency-Key header when :idempotency_key is not provided" do
42 | resp = {
43 | "id"=>"872d1f17-0f08-424c-a18c-d425324acab6", "object": "email"
44 | }
45 |
46 | allow(resp).to receive(:body).and_return(resp)
47 | allow(HTTParty).to receive(:send).and_return(resp)
48 |
49 | Resend::Batch.send([{ from: "me" }], options: {})
50 |
51 | expect(HTTParty).to have_received(:send).with(
52 | :post,
53 | "#{Resend::Request::BASE_URL}emails/batch",
54 | {
55 | headers: {
56 | "Content-Type" => "application/json",
57 | "Accept" => "application/json",
58 | "Authorization" => "Bearer re_123",
59 | "User-Agent" => "resend-ruby:#{Resend::VERSION}",
60 | },
61 | body: [{ from: "me" }].to_json
62 | }
63 | )
64 | end
65 |
66 | it "does not send the Idempotency-Key header when :idempotency_key is nil" do
67 | resp = {
68 | "id"=>"872d1f17-0f08-424c-a18c-d425324acab6", "object": "email"
69 | }
70 |
71 | allow(resp).to receive(:body).and_return(resp)
72 | allow(HTTParty).to receive(:send).and_return(resp)
73 |
74 | Resend::Batch.send([{ from: "me" }], options: { idempotency_key: nil })
75 |
76 | expect(HTTParty).to have_received(:send).with(
77 | :post,
78 | "#{Resend::Request::BASE_URL}emails/batch",
79 | {
80 | headers: {
81 | "Content-Type" => "application/json",
82 | "Accept" => "application/json",
83 | "Authorization" => "Bearer re_123",
84 | "User-Agent" => "resend-ruby:#{Resend::VERSION}",
85 | },
86 | body: [{ from: "me" }].to_json
87 | }
88 | )
89 | end
90 |
91 | it "does not send the Idempotency-Key header when :idempotency_key is an empty string" do
92 | resp = {
93 | "id"=>"872d1f17-0f08-424c-a18c-d425324acab6", "object": "email"
94 | }
95 |
96 | allow(resp).to receive(:body).and_return(resp)
97 | allow(HTTParty).to receive(:send).and_return(resp)
98 |
99 | Resend::Batch.send([{ from: "me" }], options: { idempotency_key: "" })
100 |
101 | expect(HTTParty).to have_received(:send).with(
102 | :post,
103 | "#{Resend::Request::BASE_URL}emails/batch",
104 | {
105 | headers: {
106 | "Content-Type" => "application/json",
107 | "Accept" => "application/json",
108 | "Authorization" => "Bearer re_123",
109 | "User-Agent" => "resend-ruby:#{Resend::VERSION}",
110 | },
111 | body: [{ from: "me" }].to_json
112 | }
113 | )
114 | end
115 |
116 | it "does send the Idempotency-Key header when :idempotency_key is provided" do
117 | resp = {
118 | "id"=>"872d1f17-0f08-424c-a18c-d425324acab6", "object": "email"
119 | }
120 |
121 | allow(resp).to receive(:body).and_return(resp)
122 | allow(HTTParty).to receive(:send).and_return(resp)
123 |
124 | Resend::Batch.send([{ from: "me" }], options: { idempotency_key: "123" })
125 |
126 | expect(HTTParty).to have_received(:send).with(
127 | :post,
128 | "#{Resend::Request::BASE_URL}emails/batch",
129 | {
130 | headers: {
131 | "Content-Type" => "application/json",
132 | "Accept" => "application/json",
133 | "Authorization" => "Bearer re_123",
134 | "User-Agent" => "resend-ruby:#{Resend::VERSION}",
135 | "Idempotency-Key" => "123"
136 | },
137 | body: [{ from: "me" }].to_json
138 | }
139 | )
140 | end
141 | end
142 | end
143 |
--------------------------------------------------------------------------------
/spec/broadcasts_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | RSpec.describe "Broadcasts" do
4 |
5 | before do
6 | Resend.configure do |config|
7 | config.api_key = "re_123"
8 | end
9 | end
10 |
11 | describe "create" do
12 | it "should create broadcast" do
13 | resp = {
14 | "id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794"
15 | }
16 | params = {
17 | "audience_id": "123123"
18 | }
19 | allow_any_instance_of(Resend::Request).to receive(:perform).and_return(resp)
20 | expect(Resend::Broadcasts.create(params)[:id]).to eql("49a3999c-0ce1-4ea6-ab68-afcd6dc2e794")
21 | end
22 | end
23 |
24 | describe "update" do
25 | it "should update broadcast" do
26 | resp = {
27 | "id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794"
28 | }
29 | params = {
30 | "broadcast_id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794"
31 | }
32 | allow_any_instance_of(Resend::Request).to receive(:perform).and_return(resp)
33 | expect(Resend::Broadcasts.update(params)[:id]).to eql("49a3999c-0ce1-4ea6-ab68-afcd6dc2e794")
34 | end
35 | end
36 |
37 | describe "send" do
38 | it "should send broadcast" do
39 | resp = {
40 | "id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794"
41 | }
42 | params = {
43 | "broadcast_id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794",
44 | "scheduled_at": "in 1 min"
45 | }
46 | allow_any_instance_of(Resend::Request).to receive(:perform).and_return(resp)
47 | expect(Resend::Broadcasts.send(params)[:id]).to eql("49a3999c-0ce1-4ea6-ab68-afcd6dc2e794")
48 | end
49 | end
50 |
51 | describe "list" do
52 | it "should list broadcasts" do
53 | resp = {
54 | "object": "list",
55 | "data": [
56 | {
57 | "id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794",
58 | "audience_id": "78261eea-8f8b-4381-83c6-79fa7120f1cf",
59 | "status": "draft",
60 | "created_at": "2024-11-01T15:13:31.723Z",
61 | "scheduled_at": nil,
62 | "sent_at": nil
63 | },
64 | {
65 | "id": "559ac32e-9ef5-46fb-82a1-b76b840c0f7b",
66 | "audience_id": "78261eea-8f8b-4381-83c6-79fa7120f1cf",
67 | "status": "sent",
68 | "created_at": "2024-12-01T19:32:22.980Z",
69 | "scheduled_at": "2024-12-02T19:32:22.980Z",
70 | "sent_at": "2024-12-02T19:32:22.980Z"
71 | }
72 | ]
73 | }
74 | allow_any_instance_of(Resend::Request).to receive(:perform).and_return(resp)
75 |
76 | broadcasts = Resend::Broadcasts.list[:data]
77 |
78 | expect(broadcasts.length).to eql(2)
79 |
80 | expect(broadcasts[0][:id]).to eql("49a3999c-0ce1-4ea6-ab68-afcd6dc2e794")
81 | expect(broadcasts[0][:audience_id]).to eql("78261eea-8f8b-4381-83c6-79fa7120f1cf")
82 | expect(broadcasts[0][:status]).to eql("draft")
83 | expect(broadcasts[0][:created_at]).to eql("2024-11-01T15:13:31.723Z")
84 | expect(broadcasts[0][:scheduled_at]).to eql(nil)
85 | expect(broadcasts[0][:sent_at]).to eql(nil)
86 |
87 | expect(broadcasts[1][:id]).to eql("559ac32e-9ef5-46fb-82a1-b76b840c0f7b")
88 | expect(broadcasts[1][:audience_id]).to eql("78261eea-8f8b-4381-83c6-79fa7120f1cf")
89 | expect(broadcasts[1][:status]).to eql("sent")
90 | expect(broadcasts[1][:created_at]).to eql("2024-12-01T19:32:22.980Z")
91 | expect(broadcasts[1][:scheduled_at]).to eql("2024-12-02T19:32:22.980Z")
92 | expect(broadcasts[1][:sent_at]).to eql("2024-12-02T19:32:22.980Z")
93 | end
94 | end
95 |
96 | describe "remove" do
97 | it "should remove broadcast" do
98 | allow_any_instance_of(Resend::Request).to receive(:perform).and_return("")
99 | expect { Resend::Broadcasts.remove }.not_to raise_error
100 | end
101 | end
102 |
103 | describe "get broadcast" do
104 |
105 | it "should retrieve a broadcast" do
106 |
107 | resp = {
108 | "object": "broadcast",
109 | "id": "559ac32e-9ef5-46fb-82a1-b76b840c0f7b",
110 | "name": "Announcements",
111 | "audience_id": "78261eea-8f8b-4381-83c6-79fa7120f1cf",
112 | "from": "Acme ",
113 | "subject": "hello world",
114 | "reply_to": nil,
115 | "preview_text": "Check out our latest announcements",
116 | "status": "draft",
117 | "created_at": "2024-12-01T19:32:22.980Z",
118 | "scheduled_at": nil,
119 | "sent_at": nil
120 | }
121 |
122 | allow(resp).to receive(:body).and_return(resp)
123 | allow(HTTParty).to receive(:send).and_return(resp)
124 |
125 | broadcast = Resend::Broadcasts.get("559ac32e-9ef5-46fb-82a1-b76b840c0f7b")
126 |
127 | expect(broadcast[:object]).to eql "broadcast"
128 | expect(broadcast[:id]).to eql "559ac32e-9ef5-46fb-82a1-b76b840c0f7b"
129 | expect(broadcast[:name]).to eql "Announcements"
130 | expect(broadcast[:audience_id]).to eql "78261eea-8f8b-4381-83c6-79fa7120f1cf"
131 | expect(broadcast[:from]).to eql "Acme "
132 | expect(broadcast[:subject]).to eql "hello world"
133 | expect(broadcast[:reply_to]).to eql nil
134 | expect(broadcast[:preview_text]).to eql "Check out our latest announcements"
135 | expect(broadcast[:status]).to eql "draft"
136 | expect(broadcast[:created_at]).to eql "2024-12-01T19:32:22.980Z"
137 | expect(broadcast[:scheduled_at]).to eql nil
138 | expect(broadcast[:sent_at]).to eql nil
139 | end
140 | end
141 |
142 | end
143 |
--------------------------------------------------------------------------------
/spec/contacts_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | RSpec.describe "Contacts" do
4 |
5 | let(:audience_id) { "48c269ed-9873-4d60-bdd9-cd7e6fc0b9b8" }
6 |
7 | describe "create contact" do
8 |
9 | before do
10 | Resend.configure do |config|
11 | config.api_key = "re_123"
12 | end
13 | end
14 |
15 | it "should create a contact record" do
16 | resp = {
17 | "object": "contact",
18 | "id": "479e3145-dd38-476b-932c-529ceb705947"
19 | }
20 |
21 | params = {
22 | audience_id: audience_id,
23 | email: "steve@woz.com",
24 | first_name: "Steve",
25 | last_name: "Woz",
26 | unsubscribed: false,
27 | }
28 |
29 | allow_any_instance_of(Resend::Request).to receive(:perform).and_return(resp)
30 | contact = Resend::Contacts.create(params)
31 | expect(contact[:id]).to eql("479e3145-dd38-476b-932c-529ceb705947")
32 | expect(contact[:object]).to eql("contact")
33 | end
34 | end
35 |
36 | describe "get contact" do
37 |
38 | it "should retrieve a contact by id" do
39 |
40 | resp = {
41 | "object": "contact",
42 | "id": "e169aa45-1ecf-4183-9955-b1499d5701d3",
43 | "email": "steve.wozniak@gmail.com",
44 | "first_name": "Steve",
45 | "last_name": "Wozniak",
46 | "created_at": "2023-10-06T23:47:56.678Z",
47 | "unsubscribed": false
48 | }
49 |
50 | allow(resp).to receive(:body).and_return(resp)
51 | allow(HTTParty).to receive(:send).and_return(resp)
52 |
53 | contact = Resend::Contacts.get(audience_id, resp[:id])
54 |
55 | expect(contact[:object]).to eql "contact"
56 | expect(contact[:id]).to eql "e169aa45-1ecf-4183-9955-b1499d5701d3"
57 | expect(contact[:first_name]).to eql "Steve"
58 | expect(contact[:last_name]).to eql "Wozniak"
59 | expect(contact[:email]).to eql "steve.wozniak@gmail.com"
60 | expect(contact[:unsubscribed]).to be false
61 | expect(contact[:created_at]).to eql "2023-10-06T23:47:56.678Z"
62 | end
63 |
64 | it "should retrieve a contact by email" do
65 | resp = {
66 | "object": "contact",
67 | "id": "e169aa45-1ecf-4183-9955-b1499d5701d3",
68 | "email": "steve.wozniak@gmail.com",
69 | "first_name": "Steve",
70 | "last_name": "Wozniak",
71 | "created_at": "2023-10-06T23:47:56.678Z",
72 | "unsubscribed": false
73 | }
74 |
75 | allow(resp).to receive(:body).and_return(resp)
76 | allow(HTTParty).to receive(:send).and_return(resp)
77 |
78 | contact = Resend::Contacts.get(audience_id, resp[:email])
79 |
80 | expect(contact[:object]).to eql "contact"
81 | expect(contact[:id]).to eql "e169aa45-1ecf-4183-9955-b1499d5701d3"
82 | expect(contact[:first_name]).to eql "Steve"
83 | expect(contact[:last_name]).to eql "Wozniak"
84 | expect(contact[:email]).to eql "steve.wozniak@gmail.com"
85 | expect(contact[:unsubscribed]).to be false
86 | expect(contact[:created_at]).to eql "2023-10-06T23:47:56.678Z"
87 | end
88 | end
89 |
90 | describe "list contacts" do
91 |
92 | it "should list contacts" do
93 |
94 | resp = {
95 | "object": "list",
96 | "data": [
97 | {
98 | "id": "e169aa45-1ecf-4183-9955-b1499d5701d3",
99 | "email": "steve.wozniak@gmail.com",
100 | "first_name": "Steve",
101 | "last_name": "Wozniak",
102 | "created_at": "2023-10-06T23:47:56.678Z",
103 | "unsubscribed": false
104 | }
105 | ]
106 | }
107 |
108 | allow_any_instance_of(Resend::Request).to receive(:perform).and_return(resp)
109 | contacts = Resend::Contacts.list(audience_id)
110 | expect(contacts[:object]).to eql "list"
111 | expect(contacts[:data].length).to eql 1
112 | expect(contacts[:data][0][:id]).to eql "e169aa45-1ecf-4183-9955-b1499d5701d3"
113 | expect(contacts[:data][0][:first_name]).to eql "Steve"
114 | expect(contacts[:data][0][:last_name]).to eql "Wozniak"
115 | expect(contacts[:data][0][:email]).to eql "steve.wozniak@gmail.com"
116 | expect(contacts[:data][0][:unsubscribed]).to be false
117 | end
118 | end
119 |
120 | describe "remove contact" do
121 | it "should remove contact by id" do
122 |
123 | resp = {
124 | "object": "contact",
125 | "id": "520784e2-887d-4c25-b53c-4ad46ad38100",
126 | "deleted": true
127 | }
128 |
129 | allow(resp).to receive(:body).and_return(resp)
130 | allow(HTTParty).to receive(:send).and_return(resp)
131 |
132 | deleted = Resend::Contacts.remove(audience_id, resp[:id])
133 | expect(deleted[:object]).to eql("contact")
134 | expect(deleted[:id]).to eql(resp[:id])
135 | expect(deleted[:deleted]).to be true
136 | end
137 | end
138 |
139 | describe "update contact" do
140 |
141 | before do
142 | Resend.configure do |config|
143 | config.api_key = "re_123"
144 | end
145 | end
146 |
147 | it "should update a contact record" do
148 | resp = {
149 | "object": "contact",
150 | "id": "479e3145-dd38-476b-932c-529ceb705947"
151 | }
152 |
153 | update_params = {
154 | audience_id: audience_id,
155 | id: "479e3145-dd38-476b-932c-529ceb705947",
156 | unsubscribed: false,
157 | }
158 |
159 | allow_any_instance_of(Resend::Request).to receive(:perform).and_return(resp)
160 | contact = Resend::Contacts.update(update_params)
161 | expect(contact[:id]).to eql("479e3145-dd38-476b-932c-529ceb705947")
162 | expect(contact[:object]).to eql("contact")
163 | end
164 |
165 | it "raise when required fields are not provided" do
166 | begin
167 | Resend::Contacts.update({ audience_id: "123" })
168 | rescue ArgumentError => e
169 | expect(e.message).to eql("id or email is required")
170 | end
171 | end
172 | end
173 | end
174 |
--------------------------------------------------------------------------------
/spec/domains_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | RSpec.describe "Domains" do
4 |
5 | describe "create domain" do
6 |
7 | before do
8 | Resend.configure do |config|
9 | config.api_key = "re_123"
10 | end
11 | end
12 |
13 | it "should create domain record" do
14 | resp = {
15 | "id": "4dd369bc-aa82-4ff3-97de-514ae3000ee0",
16 | "name": "example.com",
17 | "createdAt": "2023-03-28T17:12:02.059593+00:00",
18 | "status": "not_started",
19 | "records": [
20 | {
21 | "record": "SPF",
22 | "name": "bounces",
23 | "type": "MX",
24 | "ttl": "Auto",
25 | "status": "not_started",
26 | "value": "feedback-smtp.us-east-1.amazonses.com",
27 | "priority": 10
28 | },
29 | {
30 | "record": "SPF",
31 | "name": "bounces",
32 | "value": "\"v=spf1 include:amazonses.com ~all\"",
33 | "type": "TXT",
34 | "ttl": "Auto",
35 | "status": "not_started"
36 | },
37 | {
38 | "record": "DKIM",
39 | "name": "nhapbbryle57yxg3fbjytyodgbt2kyyg._domainkey",
40 | "value": "nhapbbryle57yxg3fbjytyodgbt2kyyg.dkim.amazonses.com.",
41 | "type": "CNAME",
42 | "status": "not_started",
43 | "ttl": "Auto"
44 | },
45 | {
46 | "record": "DKIM",
47 | "name": "xbakwbe5fcscrhzshpap6kbxesf6pfgn._domainkey",
48 | "value": "xbakwbe5fcscrhzshpap6kbxesf6pfgn.dkim.amazonses.com.",
49 | "type": "CNAME",
50 | "status": "not_started",
51 | "ttl": "Auto"
52 | },
53 | {
54 | "record": "DKIM",
55 | "name": "txrcreso3dqbvcve45tqyosxwaegvhgn._domainkey",
56 | "value": "txrcreso3dqbvcve45tqyosxwaegvhgn.dkim.amazonses.com.",
57 | "type": "CNAME",
58 | "status": "not_started",
59 | "ttl": "Auto"
60 | }
61 | ],
62 | "region": "us-east-1",
63 | "dnsProvider": "Unidentified"
64 | }
65 | params = {
66 | "name": "example.com",
67 | }
68 | allow_any_instance_of(Resend::Request).to receive(:perform).and_return(resp)
69 | expect(Resend::Domains.create(params)[:id]).to eql("4dd369bc-aa82-4ff3-97de-514ae3000ee0")
70 | end
71 |
72 | it "should raise when domain is already registered" do
73 | resp = {
74 | "name"=>"validation_error",
75 | "message"=>"The name domain has been registered already.",
76 | "statusCode"=>400
77 | }
78 | allow(resp).to receive(:body).and_return(resp)
79 | params = {
80 | "name": "example.com",
81 | }
82 | allow(HTTParty).to receive(:send).and_return(resp)
83 | expect { Resend::Domains.create params }.to raise_error(Resend::Error::InvalidRequestError, /The name domain has been registered already./)
84 | end
85 | end
86 |
87 | describe "update domain" do
88 |
89 | before do
90 | Resend.configure do |config|
91 | config.api_key = "re_123"
92 | end
93 | end
94 |
95 | it "should update a domain record" do
96 | resp = {
97 | "object": "domain",
98 | "id": "479e3145-dd38-476b-932c-529ceb705947"
99 | }
100 |
101 | update_params = {
102 | id: '479e3145-dd38-476b-932c-529ceb705947',
103 | open_tracking: true,
104 | click_tracking: true,
105 | tls: "enforced"
106 | }
107 |
108 | allow_any_instance_of(Resend::Request).to receive(:perform).and_return(resp)
109 | domain = Resend::Domains.update(update_params)
110 | expect(domain[:id]).to eql("479e3145-dd38-476b-932c-529ceb705947")
111 | expect(domain[:object]).to eql("domain")
112 | end
113 | end
114 |
115 | describe "get domain" do
116 | it "should retrieve domain" do
117 | resp = {
118 | "object": "domain",
119 | "id": "d91cd9bd-1176-453e-8fc1-35364d380206",
120 | "name": "example.com",
121 | "status": "not_started",
122 | "created_at": "2023-04-26T20:21:26.347412+00:00",
123 | "region": "us-east-1"
124 | }
125 | allow(resp).to receive(:body).and_return(resp)
126 | allow(HTTParty).to receive(:send).and_return(resp)
127 |
128 | email = Resend::Domains.get(resp[:id])
129 |
130 | expect(email[:object]).to eql "domain"
131 | expect(email[:id]).to eql "d91cd9bd-1176-453e-8fc1-35364d380206"
132 | expect(email[:name]).to eql "example.com"
133 | expect(email[:status]).to eql "not_started"
134 | expect(email[:created_at]).to eql "2023-04-26T20:21:26.347412+00:00"
135 | expect(email[:region]).to eql "us-east-1"
136 | end
137 | end
138 |
139 | describe "list domains" do
140 | it "should list domains" do
141 | resp = {
142 | "data": [
143 | {
144 | "id": "d91cd9bd-1176-453e-8fc1-35364d380206",
145 | "name": "example.com",
146 | "status": "not_started",
147 | "created_at": "2023-04-26T20:21:26.347412+00:00",
148 | "region": "us-east-1"
149 | }
150 | ]
151 | }
152 | allow_any_instance_of(Resend::Request).to receive(:perform).and_return(resp)
153 | expect(Resend::Domains.list[:data].empty?).to be false
154 | expect(Resend::Domains.list[:data].length).to eql(1)
155 | end
156 | end
157 |
158 | describe "remove domain" do
159 | it "should remove domain by domain id" do
160 | allow_any_instance_of(Resend::Request).to receive(:perform).and_return("")
161 | expect { Resend::Domains.remove }.not_to raise_error
162 | end
163 | end
164 |
165 | describe "verify domain" do
166 | it "should verify domain by domain id" do
167 | allow_any_instance_of(Resend::Request).to receive(:perform).and_return("")
168 | expect { Resend::Domains.verify }.not_to raise_error
169 | end
170 | end
171 | end
172 |
--------------------------------------------------------------------------------
/spec/emails_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | RSpec.describe "Emails" do
4 |
5 | describe "send_email" do
6 |
7 | before do
8 | Resend.api_key = "re_123"
9 | end
10 |
11 | it "should send email" do
12 | resp = {"id"=>"872d1f17-0f08-424c-a18c-d425324acab6"}
13 | params = {
14 | "from": "from@e.io",
15 | "to": ["email1@email.com"],
16 | "text": "test",
17 | "subject": "test",
18 | "tags": {
19 | "country": "br"
20 | }
21 | }
22 | allow_any_instance_of(Resend::Request).to receive(:perform).and_return(resp)
23 | expect(Resend::Emails.send(params)[:id]).to eql(resp[:id])
24 | end
25 |
26 | it "should update email" do
27 | resp = {"id"=>"872d1f17-0f08-424c-a18c-d425324acab6", "object": "email"}
28 | params = {
29 | "id": "872d1f17-0f08-424c-a18c-d425324acab6",
30 | "scheduled_at": "2024-11-05T11:52:01.858Z"
31 | }
32 | allow_any_instance_of(Resend::Request).to receive(:perform).and_return(resp)
33 | expect(Resend::Emails.update(params)[:id]).to eql(resp[:id])
34 | end
35 |
36 | it "should retrieve email" do
37 | resp = {
38 | "object": "email",
39 | "id": "4ef9a417-02e9-4d39-ad75-9611e0fcc33c",
40 | "to": ["james@bond.com"],
41 | "from": "onboarding@resend.dev",
42 | "created_at": "2023-04-03T22:13:42.674981+00:00",
43 | "subject": "Hello World",
44 | "html": "Congrats on sending your first email!",
45 | "text": nil,
46 | "bcc": [nil],
47 | "cc": [nil],
48 | "reply_to": [nil],
49 | "last_event": "delivered"
50 | }
51 | allow(resp).to receive(:body).and_return(resp)
52 | allow(HTTParty).to receive(:send).and_return(resp)
53 | email = Resend::Emails.get(resp[:id])
54 | expect(email[:subject]).to eql "Hello World"
55 | expect(email[:id]).to eql "4ef9a417-02e9-4d39-ad75-9611e0fcc33c"
56 | end
57 |
58 | it "should cancel email" do
59 | resp = {
60 | "object": "email",
61 | "id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794"
62 | }
63 | allow(resp).to receive(:body).and_return(resp)
64 | allow(HTTParty).to receive(:send).and_return(resp)
65 |
66 | email = Resend::Emails.cancel(resp[:id])
67 | expect(email[:id]).to eql "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794"
68 | end
69 |
70 | it "should raise when to is missing" do
71 | resp = {
72 | "statusCode"=>422,
73 | "name"=>"missing_required_field",
74 | "message"=>"Missing `to` field"
75 | }
76 | allow(resp).to receive(:body).and_return(resp)
77 | params = {
78 | "from": "from@e.io",
79 | "text": "test",
80 | "subject": "test",
81 | "tags": {
82 | "country": "br"
83 | }
84 | }
85 | allow(HTTParty).to receive(:send).and_return(resp)
86 | expect { Resend::Emails.send params }.to raise_error(Resend::Error::InvalidRequestError, /Missing `to` field/)
87 | end
88 |
89 | it "should raise when from is missing" do
90 | resp = {
91 | "statusCode"=>422,
92 | "name"=>"missing_required_field",
93 | "message"=>"Missing `from` field"
94 | }
95 | allow(resp).to receive(:body).and_return(resp)
96 | params = {
97 | "to": ["from@e.io"],
98 | "text": "test",
99 | "subject": "test",
100 | "tags": {
101 | "country": "br"
102 | }
103 | }
104 | allow(HTTParty).to receive(:send).and_return(resp)
105 | expect { Resend::Emails.send params }.to raise_error(Resend::Error::InvalidRequestError, /Missing `from` field/)
106 | end
107 |
108 | it "does not send the Idempotency-Key header when :idempotency_key is not provided" do
109 | resp = {
110 | "id"=>"872d1f17-0f08-424c-a18c-d425324acab6", "object": "email"
111 | }
112 |
113 | allow(resp).to receive(:body).and_return(resp)
114 | allow(HTTParty).to receive(:send).and_return(resp)
115 |
116 | Resend::Emails.send({ from: "me" })
117 |
118 | expect(HTTParty).to have_received(:send).with(
119 | :post,
120 | "#{Resend::Request::BASE_URL}emails",
121 | {
122 | headers: {
123 | "Content-Type" => "application/json",
124 | "Accept" => "application/json",
125 | "Authorization" => "Bearer re_123",
126 | "User-Agent" => "resend-ruby:#{Resend::VERSION}",
127 | },
128 | body: { from: "me" }.to_json
129 | }
130 | )
131 | end
132 |
133 | it "does not send the Idempotency-Key header when :idempotency_key is nil" do
134 | resp = {
135 | "id"=>"872d1f17-0f08-424c-a18c-d425324acab6", "object": "email"
136 | }
137 |
138 | allow(resp).to receive(:body).and_return(resp)
139 | allow(HTTParty).to receive(:send).and_return(resp)
140 |
141 | Resend::Emails.send({ from: "me" }, options: { idempotency_key: nil })
142 |
143 | expect(HTTParty).to have_received(:send).with(
144 | :post,
145 | "#{Resend::Request::BASE_URL}emails",
146 | {
147 | headers: {
148 | "Content-Type" => "application/json",
149 | "Accept" => "application/json",
150 | "Authorization" => "Bearer re_123",
151 | "User-Agent" => "resend-ruby:#{Resend::VERSION}",
152 | },
153 | body: { from: "me" }.to_json
154 | }
155 | )
156 | end
157 |
158 | it "does not send the Idempotency-Key header when :idempotency_key is an empty string" do
159 | resp = {
160 | "id"=>"872d1f17-0f08-424c-a18c-d425324acab6", "object": "email"
161 | }
162 |
163 | allow(resp).to receive(:body).and_return(resp)
164 | allow(HTTParty).to receive(:send).and_return(resp)
165 |
166 | Resend::Emails.send({ from: "me" }, options: { idempotency_key: "" })
167 |
168 | expect(HTTParty).to have_received(:send).with(
169 | :post,
170 | "#{Resend::Request::BASE_URL}emails",
171 | {
172 | headers: {
173 | "Content-Type" => "application/json",
174 | "Accept" => "application/json",
175 | "Authorization" => "Bearer re_123",
176 | "User-Agent" => "resend-ruby:#{Resend::VERSION}",
177 | },
178 | body: { from: "me" }.to_json
179 | }
180 | )
181 | end
182 |
183 | it "does send the Idempotency-Key header when :idempotency_key is provided" do
184 | resp = {
185 | "id"=>"872d1f17-0f08-424c-a18c-d425324acab6", "object": "email"
186 | }
187 |
188 | allow(resp).to receive(:body).and_return(resp)
189 | allow(HTTParty).to receive(:send).and_return(resp)
190 |
191 | Resend::Emails.send({ from: "me" }, options: { idempotency_key: "123" })
192 |
193 | expect(HTTParty).to have_received(:send).with(
194 | :post,
195 | "#{Resend::Request::BASE_URL}emails",
196 | {
197 | headers: {
198 | "Content-Type" => "application/json",
199 | "Accept" => "application/json",
200 | "Authorization" => "Bearer re_123",
201 | "User-Agent" => "resend-ruby:#{Resend::VERSION}",
202 | "Idempotency-Key" => "123"
203 | },
204 | body: { from: "me" }.to_json
205 | }
206 | )
207 | end
208 | end
209 | end
210 |
--------------------------------------------------------------------------------
/spec/railtie/mailer_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "rails"
4 | require "resend"
5 | require "resend/railtie"
6 | require "spec_helper"
7 | require "action_mailer"
8 | require "pry-byebug"
9 |
10 | ActionMailer::Base.raise_delivery_errors = true
11 | ActionMailer::Base.delivery_method = :test
12 |
13 | class TestMailer < ActionMailer::Base
14 | default from: "test@example.com"
15 |
16 | def html_text_msg(to, subject)
17 | headers = {
18 | "X-Entity-Ref-ID": "123",
19 | }
20 | mail(to: to, subject: subject, headers: headers) do |format|
21 | format.html { render html: "HTML!
".html_safe }
22 | format.text { render plain: "text" }
23 | end
24 | end
25 |
26 | def text_only(to, subject)
27 | mail(to: to, subject: subject) do |format|
28 | format.text { render plain: "text" }
29 | end
30 | end
31 |
32 | def html_only(to, subject)
33 | mail(to: to, subject: subject) do |format|
34 | format.html { render html: "HTML!
".html_safe }
35 | end
36 | end
37 |
38 | def with_headers_method(to, subject)
39 | headers["X-Entity-Ref-ID"] = "123"
40 | mail(to: to, subject: subject) do |format|
41 | format.text { render plain: "txt" }
42 | format.html { render html: "html
".html_safe }
43 | end
44 | end
45 |
46 | def with_overwritten_headers(to, subject)
47 | headers["X-Entity-Ref-ID"] = "123"
48 | mail(to: to, subject: subject, headers: {"X-Entity-Ref-ID": "overwritten"}) do |format|
49 | format.text { render plain: "txt" }
50 | format.html { render html: "html
".html_safe }
51 | end
52 | end
53 |
54 | def with_nil_header_values(to, subject)
55 | headers["X-Entity-Ref-ID"] = nil
56 | mail(to: to, subject: subject, headers: {
57 | "custom-header1": nil,
58 | "custom-header2": "v2"
59 | }) do |format|
60 | format.text { render plain: "txt" }
61 | format.html { render html: "html
".html_safe }
62 | end
63 | end
64 |
65 | def with_attachment(to, subject)
66 | attachments['invoice.pdf'] = {
67 | :content => File.read('resources/invoice.pdf'),
68 | :mime_type => 'application/pdf',
69 | }
70 | mail(to: to, subject: subject) do |format|
71 | format.text { render plain: "txt" }
72 | format.html { render html: "html
".html_safe }
73 | end
74 | end
75 |
76 | def with_options_hash
77 | headers = {
78 | "X-Entity-Ref-ID": "123",
79 | }
80 | options = {
81 | idempotency_key: "123",
82 | ignored_key: "ignored",
83 | }
84 | mail(to: "to", subject: "subj", headers: headers, options: options) do |format|
85 | format.text { render plain: "text" }
86 | end
87 | end
88 | end
89 |
90 | class TestMailerWithDisplayName < TestMailer
91 | default from: "Test "
92 | end
93 |
94 | RSpec.describe "Resend::Mailer" do
95 | before do
96 | @mailer = Resend::Mailer.new({ api_key: "123" })
97 | end
98 |
99 | it "properly creates a message body" do
100 | message = TestMailer.html_text_msg("test@example.org", "Test!")
101 | body = @mailer.build_resend_params(message)
102 | expect(body[:from]).to eql("test@example.com")
103 | expect(body[:to]).to eql(["test@example.org"])
104 | expect(body[:html]).to eql("HTML!
")
105 | expect(body[:text]).to eql("text")
106 | expect(body[:headers]["X-Entity-Ref-ID"]).to eql("123")
107 | end
108 |
109 | it "properly creates a html only msg" do
110 | message = TestMailer.html_only("test@example.org", "Test!")
111 | body = @mailer.build_resend_params(message)
112 | expect(body[:from]).to eql("test@example.com")
113 | expect(body[:to]).to eql(["test@example.org"])
114 | expect(body[:html]).to eql("HTML!
")
115 | expect(body[:text]).to be nil
116 | end
117 |
118 | it "properly creates options hash" do
119 | message = TestMailer.with_options_hash
120 | options = @mailer.get_options(message)
121 | expect(options[:idempotency_key]).to eql("123")
122 | expect(options[:ignored_key]).to be nil
123 | expect(options.keys).to match_array([:idempotency_key])
124 | end
125 |
126 | it "properly creates a text only msg" do
127 | message = TestMailer.text_only("test@example.org", "Test!")
128 | body = @mailer.build_resend_params(message)
129 | expect(body[:from]).to eql("test@example.com")
130 | expect(body[:to]).to eql(["test@example.org"])
131 | expect(body[:html]).to be nil
132 | expect(body[:text]).to eql("text")
133 | end
134 |
135 | it "properly creates a html text with attachments msg" do
136 | message = TestMailer.with_attachment("test@example.org", "Test!")
137 | body = @mailer.build_resend_params(message)
138 | expect(body[:from]).to eql("test@example.com")
139 | expect(body[:to]).to eql(["test@example.org"])
140 | expect(body[:html]).to eql("html
")
141 | expect(body[:text]).to eql("txt")
142 | expect(body[:attachments].length).to eql(1)
143 | expect(body[:attachments].first[:filename]).to eql("invoice.pdf")
144 | expect(body[:attachments].first[:content].length > 0).to be true
145 | end
146 |
147 | it "properly handles from display name" do
148 | message = TestMailerWithDisplayName
149 | .text_only("test@example.org", "Test!")
150 |
151 | body = @mailer.build_resend_params(message)
152 | expect(body[:from]).to eql("Test ")
153 | expect(body[:to]).to eql(["test@example.org"])
154 | expect(body[:html]).to be nil
155 | expect(body[:text]).to eql("text")
156 | expect(body[:headers]).to be nil
157 | end
158 |
159 | it "properly creates header through headers method" do
160 | message = TestMailer.with_headers_method("test@example.org", "Test!")
161 | body = @mailer.build_resend_params(message)
162 | expect(body[:from]).to eql("test@example.com")
163 | expect(body[:to]).to eql(["test@example.org"])
164 | expect(body[:html]).to eql("html
")
165 | expect(body[:text]).to eql("txt")
166 | expect(body[:headers]["X-Entity-Ref-ID"]).to eql("123")
167 | end
168 |
169 | it "#build_resend_params properly overwrites #headers" do
170 | message = TestMailer.with_overwritten_headers("test@example.org", "Test!")
171 | body = @mailer.build_resend_params(message)
172 | expect(body[:from]).to eql("test@example.com")
173 | expect(body[:tags]).to eql(nil)
174 | expect(body[:to]).to eql(["test@example.org"])
175 | expect(body[:html]).to eql("html
")
176 | expect(body[:text]).to eql("txt")
177 | expect(body[:headers]["X-Entity-Ref-ID"]).to eql("overwritten")
178 | end
179 |
180 | it "#build_resend_params handles nil values" do
181 | message = TestMailer.with_nil_header_values("test@example.org", "Test!")
182 | body = @mailer.build_resend_params(message)
183 | expect(body[:headers].keys).to eq(["custom-header2"])
184 | end
185 | end
186 |
--------------------------------------------------------------------------------
/spec/request_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "resend/request"
4 | require "resend/client"
5 |
6 | RSpec.describe Resend::Request do
7 |
8 | before do
9 | Resend.api_key = "re_123"
10 | end
11 |
12 | context '#perform' do
13 | context 'response is not json' do
14 | before do
15 | response = double(HTTParty::Response)
16 | html = "not json"
17 | allow(response).to receive(:body).and_return(html)
18 | allow(HTTParty).to receive(:send).and_return(response)
19 | end
20 |
21 | it 'should be rescuable' do
22 | req = described_class.new
23 | expect { req.perform }.to raise_error(Resend::Error::InternalServerError, /Resend API returned an unexpected response/)
24 | end
25 | end
26 | end
27 |
28 | context 'initialize' do
29 | it 'sets default headers' do
30 | req = described_class.new
31 | expect(req.instance_variable_get(:@headers)).to include(
32 | "Content-Type" => "application/json",
33 | "Accept" => "application/json",
34 | "User-Agent" => "resend-ruby:#{Resend::VERSION}",
35 | "Authorization" => "Bearer re_123"
36 | )
37 | expect(req.instance_variable_get(:@headers)).not_to include("Idempotency-Key")
38 | end
39 |
40 | it 'should set idempotency key' do
41 | req = described_class.new("path", {}, "POST", options: { idempotency_key: "123" })
42 | expect(req.instance_variable_get(:@headers)["Idempotency-Key"]).to eq("123")
43 | end
44 |
45 | it 'sets verb' do
46 | req = described_class.new("path", {}, "POST")
47 | expect(req.instance_variable_get(:@verb)).to eq("POST")
48 | end
49 | end
50 |
51 | context 'handle_error!' do
52 |
53 | it "Resend::Error::InvalidRequestError 400" do
54 | req = described_class.new
55 | resp = {
56 | :statusCode => 400,
57 | :message => "400"
58 | }
59 | expect { req.handle_error!(resp) }.to raise_error(Resend::Error::InvalidRequestError, /400/)
60 | end
61 |
62 | it "Resend::Error::InvalidRequestError 422" do
63 | req = described_class.new
64 | resp = {
65 | :statusCode => 422,
66 | :message => "422"
67 | }
68 | expect { req.handle_error!(resp) }.to raise_error(Resend::Error::InvalidRequestError, /422/)
69 | end
70 |
71 | it "Resend::Error::RateLimitExceededError 429" do
72 | req = described_class.new
73 | resp = {
74 | :statusCode => 429,
75 | :message => "429"
76 | }
77 | expect { req.handle_error!(resp) }.to raise_error(Resend::Error::RateLimitExceededError, /429/)
78 | end
79 |
80 | it "Resend::Error::InternalServerError 500" do
81 | req = described_class.new
82 | resp = {
83 | :statusCode => 500,
84 | :message => "500"
85 | }
86 | expect { req.handle_error!(resp) }.to raise_error(Resend::Error::InternalServerError, /500/)
87 | end
88 |
89 | it 'unkown error should return Resend::Error' do
90 | req = described_class.new
91 | resp = {
92 | :statusCode => 999,
93 | :message => "999"
94 | }
95 | expect { req.handle_error!(resp) }.to raise_error(Resend::Error, "999")
96 | end
97 | end
98 | end
99 |
--------------------------------------------------------------------------------
/spec/resend_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | RSpec.describe Resend do
4 | it "has a version number" do
5 | expect(Resend::VERSION).not_to be nil
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "resend"
4 |
5 | RSpec.configure do |config|
6 | # Enable flags like --only-failures and --next-failure
7 | config.example_status_persistence_file_path = ".rspec_status"
8 |
9 | # Disable RSpec exposing methods globally on `Module` and `main`
10 | config.disable_monkey_patching!
11 |
12 | config.expect_with :rspec do |c|
13 | c.syntax = :expect
14 | end
15 |
16 | config.filter_run_when_matching :focus
17 | end
18 |
--------------------------------------------------------------------------------