lib/openxml/docx/package.rb in openxml-docx-0.9.0 vs lib/openxml/docx/package.rb in openxml-docx-0.10.0
- old
+ new
@@ -3,34 +3,106 @@
module OpenXml
module Docx
class Package < OpenXml::Package
attr_reader :document,
:doc_rels,
+ :font_rels,
:settings,
- :styles
+ :headers,
+ :footers,
+ :styles,
+ :fonts
content_types do
default "xml", TYPE_XML
+ default "odttf", TYPE_OBSCURED_FONT
override "/word/styles.xml", TYPE_STYLES
override "/word/settings.xml", TYPE_SETTINGS
+ override "/word/fontTable.xml", TYPE_FONT_TABLE
end
def initialize
super
rels.add_relationship REL_DOCUMENT, "/word/document.xml"
@doc_rels = OpenXml::Parts::Rels.new
+ @font_rels = OpenXml::Parts::Rels.new
@settings = OpenXml::Docx::Parts::Settings.new
@styles = OpenXml::Docx::Parts::Styles.new
+ @fonts = OpenXml::Docx::Parts::Fonts.new
@document = OpenXml::Docx::Parts::Document.new
+ @headers = []
+ @footers = []
doc_rels.add_relationship REL_STYLES, "styles.xml"
doc_rels.add_relationship REL_SETTINGS, "settings.xml"
+ doc_rels.add_relationship REL_FONT_TABLE, "fontTable.xml"
add_part "word/_rels/document.xml.rels", doc_rels
+ add_part "word/_rels/fontTable.xml.rels", font_rels
add_part "word/document.xml", document
add_part "word/settings.xml", settings
add_part "word/styles.xml", styles
+ add_part "word/fontTable.xml", fonts
+ end
+
+ def embed_truetype_font(path: nil, name: nil)
+ File.open(path, "rb") do |source_font|
+ obfuscation_data = obfuscate_font source_font
+ data = obfuscation_data[:bytes] << source_font.read
+ destination_font_name = "font#{fonts.fonts.count + 1}.odttf"
+ add_part "word/fonts/#{destination_font_name}", OpenXml::Parts::UnparsedPart.new(data)
+ font_relationship = font_rels.add_relationship REL_FONT, "fonts/#{destination_font_name}"
+
+ font_description = OpenXml::Docx::Elements::Font.new
+ font_description.font_name = name
+ embed_tag = OpenXml::Docx::Elements::EmbedRegular.new
+ embed_tag.font_key = "{#{obfuscation_data[:key]}}"
+ embed_tag.relationship_id = font_relationship.id
+ font_description << embed_tag
+ fonts << font_description
+ end
+ end
+
+ def add_header(header)
+ headers << header
+ header_name = "header#{headers.count}.xml"
+ Package.content_types { override "/word/#{header_name}", TYPE_HEADER }
+ add_part "word/#{header_name}", header
+ relationship = doc_rels.add_relationship REL_HEADER, header_name
+ relationship.id
+ end
+
+ def add_footer(footer)
+ footers << footer
+ footer_name = "footer#{footers.count}.xml"
+ Package.content_types { override "/word/#{footer_name}", TYPE_FOOTER }
+ add_part "word/#{footer_name}", footer
+ relationship = doc_rels.add_relationship REL_FOOTER, footer_name
+ relationship.id
+ end
+
+ private
+
+ def obfuscate_font(font)
+ # From the OpenXml spec, section 17.8.1, the algorithm for obfuscating a font:
+ # - Generate a GUID, which is used and stored as the obfuscation key
+ # - Reverse the order of the bytes in the GUID (i.e. Big Endian ordering)
+ # - XOR the value with the first 32 bytes of the binary: once against 0-15, once against 16-31
+ # - Store the resulting file in the document, and store the obfuscation key in the fontKey attribute
+
+ key = SecureRandom::uuid.upcase # Spec requires hex characters be uppercase
+ raw_key = key.gsub("-", "")
+ big_endian_key = [raw_key].pack("H*").bytes.reverse
+ obfuscated_bytes = []
+ 2.times do
+ bytes = font.read(16).bytes
+ (0...16).each do |index|
+ obfuscated_bytes << (bytes[index] ^ big_endian_key[index])
+ end
+ end
+
+ { key: key, bytes: obfuscated_bytes.pack("C*") }
end
end
end
end