Class: ActiveStorage::Previewer::OfficePreviewer

Inherits:
ActiveStorage::Previewer
  • Object
show all
Defined in:
lib/active_storage/previewer/office_previewer.rb

Constant Summary collapse

ACCEPTABLE_CONTENT_TYPES =
[
  "application/msword", # .doc
  "application/vnd.openxmlformats-officedocument.wordprocessingml.document", # .docx

  "application/vnd.ms-powerpoint", # .ppt
  "application/vnd.openxmlformats-officedocument.presentationml.presentation", # .pptx

  "application/vnd.ms-excel", # .xls
  "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" # .xlsx
]
PRESERVED_ENV_KEYS =

LibreOffice is launched with an explicit, minimal environment rather than the full environment of the host process. Only these variables are forwarded; every other variable — application secrets such as RAILS_MASTER_KEY, DATABASE_URL, or cloud credentials in particular — is withheld from the child process. Only the set of names is fixed here: the values are taken from the parent environment, so each deployment keeps its own locale, paths, and temporary directory.

%w[
  PATH HOME LANG LANGUAGE LC_ALL LC_CTYPE LC_MESSAGES LC_NUMERIC TMPDIR TMP TEMP USER LOGNAME
]
REGISTRY_MODIFICATIONS =

Seeded into a throwaway LibreOffice user profile for every conversion.

DisableActiveContent turns off "active content" — LibreOffice's term for OLE and DDE objects and other embedded executable content.

BlockUntrustedRefererLinks refuses externally linked resources. Without it a document may reference a file by link and LibreOffice resolves that reference during conversion. The regression test in test/office_previewer_test.rb covers this path. Do not remove either key without re-running that test.

Both keys are set deliberately. The measured drop in rendered ink (0.0965 to 0.0047 on LibreOffice 24.2.7.2) was obtained with DisableActiveContent alone, so that is the key the measurement exercises. BlockUntrustedRefererLinks is set as well because it is the documented lever for linked resources, and relying on a single observed behaviour across LibreOffice versions is not worth the risk.

<<~XCU
  <?xml version="1.0" encoding="UTF-8"?>
  <oor:items xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <item oor:path="/org.openoffice.Office.Common/Security/Scripting">
    <prop oor:name="DisableActiveContent" oor:op="fuse"><value>true</value></prop>
    <prop oor:name="BlockUntrustedRefererLinks" oor:op="fuse"><value>true</value></prop>
   </item>
  </oor:items>
XCU

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.accept?(blob) ⇒ Boolean

Returns:

  • (Boolean)


53
54
55
# File 'lib/active_storage/previewer/office_previewer.rb', line 53

def accept?(blob)
  blob.content_type.in?(ACCEPTABLE_CONTENT_TYPES) && soffice_exists?
end

.scrubbed_envObject

The minimal environment handed to LibreOffice, drawn from the parent process.



68
69
70
# File 'lib/active_storage/previewer/office_previewer.rb', line 68

def scrubbed_env
  ENV.slice(*PRESERVED_ENV_KEYS)
end

.soffice_exists?Boolean

Returns:

  • (Boolean)


57
58
59
60
61
# File 'lib/active_storage/previewer/office_previewer.rb', line 57

def soffice_exists?
  return @soffice_exists if defined?(@soffice_exists)

  @soffice_exists = system(scrubbed_env, soffice_path, "--version", out: File::NULL, err: File::NULL, unsetenv_others: true)
end

.soffice_pathObject



63
64
65
# File 'lib/active_storage/previewer/office_previewer.rb', line 63

def soffice_path
  ActiveStorage.paths[:soffice] || "soffice"
end

Instance Method Details

#preview(**options) ⇒ Object



73
74
75
76
77
78
79
# File 'lib/active_storage/previewer/office_previewer.rb', line 73

def preview(**options)
  download_blob_to_tempfile do |input|
    draw_poster_image_from input do |output|
      yield io: output, filename: "#{blob.filename.base}.png", content_type: "image/png", **options
    end
  end
end